<commit_before>// Copyright 2017 Google Inc.
//
// 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.

mod cmd_finder;

use std::env;
use std::env::consts;
use std::fs;
use std::path::{Path, PathBuf};

static COMBINED_LIB: &str = "shaderc_combined";
static DYNAMIC_LIB: &str = "shaderc_shared";
static COMBINED_LIB_FILE: &str = "libshaderc_combined.a";
static SPIRV_LIB_FILE: &str = "libSPIRV.a";

fn build_shaderc(shaderc_dir: &PathBuf, use_ninja: bool) -> PathBuf {
    let mut config = cmake::Config::new(shaderc_dir);
    config
        .profile("Release")
        .define("CMAKE_POSITION_INDEPENDENT_CODE", "ON")
        .define("SPIRV_SKIP_EXECUTABLES", "ON")
        .define("SPIRV_WERROR", "OFF")
        .define("SHADERC_SKIP_TESTS", "ON")
        .define("CMAKE_INSTALL_LIBDIR", "lib");
    if use_ninja {
        config.generator("Ninja");
    }
    config.build()
}

fn build_shaderc_msvc(shaderc_dir: &PathBuf) -> PathBuf {
    let mut config = cmake::Config::new(shaderc_dir);
    config
        .profile("Release")
        .define("CMAKE_POSITION_INDEPENDENT_CODE", "ON")
        .define("SPIRV_SKIP_EXECUTABLES", "ON")
        .define("SPIRV_WERROR", "OFF")
        .define("SHADERC_SKIP_TESTS", "ON")
        // cmake-rs tries to be clever on Windows by injecting several
        // C/C++ flags, which causes problems. So I have to manually
        // define CMAKE_*_FLAGS_* here to suppress that.
        .define("CMAKE_C_FLAGS", " /nologo /EHsc /MD")
        .define("CMAKE_CXX_FLAGS", " /nologo /EHsc /MD")
        .define("CMAKE_C_FLAGS_RELEASE", " /nologo /EHsc /MD")
        .define("CMAKE_CXX_FLAGS_RELEASE", " /nologo /EHsc /MD")

        // prevent shaderc's cmake script messes with crt flags
        .define("SHADERC_ENABLE_SHARED_CRT", "ON")
        .define("CMAKE_INSTALL_LIBDIR", "lib")
        .generator("Ninja");
    config.build()
}

fn main() {
    let config_build_from_source = env::var("CARGO_FEATURE_BUILD_FROM_SOURCE").is_ok();
    let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
    let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap();
    let explicit_lib_dir_set = env::var("SHADERC_LIB_DIR").is_ok();

    // Deprecated --no-defaults behavior from before shaderc-rs & shaderc-sys split
    // This block is only relevant to shaderc-rs but will skip all subsequent
    // build options
    if env::var("CARGO_FEATURE_CHECK_INVERTED_NO_DEFAULTS").is_ok()
        && env::var("CARGO_FEATURE_INVERTED_NO_DEFAULTS").is_err()
    {
        let out_dir = env::var("OUT_DIR").unwrap();
        println!("cargo:warning=USE OF --no-default-features IS DEPRECATED BEHAVIOR.");
        println!(
            "cargo:warning=Requested to use cargo out directory to find libshaderc_combined.a"
        );
        println!(
            "cargo:warning=Searching {} for shaderc_combined static lib...",
            out_dir
        );
        println!("cargo:rustc-link-search=native={}", out_dir);
        println!("cargo:rustc-link-lib=static=shaderc_combined");
        emit_std_cpp_link();
        return;
    }

    // Initialize explicit libshaderc search directory first
    let mut search_dir = if let Ok(lib_dir) = env::var("SHADERC_LIB_DIR") {
        println!(
            "cargo:warning=Specified {} to search for libshaderc.",
            lib_dir
        );
        Some(lib_dir)
    } else {
        None
    };

    // If no explicit path is set and no explicit request is made to build from
    // source, check known locations before falling back to from-source-build
    if search_dir.is_none() && target_os == "linux" && !config_build_from_source {
        println!(
            "cargo:warning=Checking for system installed libraries.  \
             Use --features = build-from-source to disable this behavior"
        );
        search_dir = Some("/usr/lib/".to_owned());
    }

    // Try to build with the static lib if a path was received or chosen
    let search_dir = if let Some(search_dir) = search_dir {
        let path = Path::new(&search_dir);
        let cannonical = fs::canonicalize(&path);
        if path.is_relative() {
            println!(
                "cargo:warning=Provided path {:?} is relative.  Path must be \
                 relative to shaderc-sys crate, possibly not your current \
                 working directory",
                &path
            );
        } else if path.is_dir() == false {
            println!(
                "cargo:warning=Provided path {:?} is not a directory.",
                &path
            );
        }
        if (cannonical.is_err()) && explicit_lib_dir_set {
            println!("cargo:warning={:?}", cannonical.err().unwrap());
            println!(
                "cargo:warning=Provided path {:?} could not be canonicallized",
                &path
            );
            None
        } else {
            cannonical.ok()
        }
    } else {
        None
    };

    if let Some(search_dir) = search_dir {
        let search_dir_str = search_dir.to_string_lossy();
        let combined_lib_path = search_dir.join(COMBINED_LIB_FILE);
        let dylib_name = format!("{}{}{}", consts::DLL_PREFIX, DYNAMIC_LIB, consts::DLL_SUFFIX);
        let dylib_path = search_dir.join(dylib_name.clone());

        if let Some((lib_dir, lib_name, kind)) = {
            if combined_lib_path.exists() {
                Some((&search_dir_str, COMBINED_LIB, "static"))
            } else if dylib_path.exists() {
                Some((&search_dir_str, DYNAMIC_LIB, "dylib"))
            } else {
                None
            }
        } {
            match (target_os.as_str(), target_env.as_str()) {
                ("linux", _) => {
                    println!("cargo:rustc-link-search=native={}", lib_dir);
                    let spirv_path = search_dir.join(SPIRV_LIB_FILE);
                    if spirv_path.exists() {
                        println!(
                            "cargo:warning=Found SPIRV.  Linking libSPIRV & \
                             libglslang"
                        );
                        println!("cargo:rustc-link-lib=static=SPIRV");
                        println!("cargo:rustc-link-lib=static=SPIRV-Tools-opt");
                        println!("cargo:rustc-link-lib=static=SPIRV-Tools");
                        println!("cargo:rustc-link-lib=glslang");
                    } else {
                        println!(
                            "cargo:warning=Only libshaderc library found.  \
                             Assuming libSPIRV & libglslang built into provided \
                             libshaderc."
                        );
                    }
                    println!("cargo:rustc-link-lib={}={}", kind, lib_name);
                    println!("cargo:rustc-link-lib=dylib=stdc++");
                    return;
                }
                ("windows", "gnu") => {
                    println!(
                        "cargo:warning=Windows MinGW static builds \
                         experimental"
                    );
                    println!("cargo:rustc-link-search=native={}", lib_dir);
                    println!("cargo:rustc-link-lib={}={}", kind, lib_name);
                    println!("cargo:rustc-link-lib=dylib=stdc++");
                    return;
                }
                ("macos", _) => {
                    println!("cargo:warning=MacOS static builds experimental");
                    println!("cargo:rustc-link-search=native={}", lib_dir);
                    println!("cargo:rustc-link-lib={}={}", kind, lib_name);
                    println!("cargo:rustc-link-lib=dylib=c++");
                    return;
                }
                (_, _) => {
                    println!(
                        "cargo:warning=Platform unsupported for pre-built \
                         libshaderc"
                    );
                }
            }
        }
    }

    if config_build_from_source {
        println!("cargo:warning=Requested to build from source");
    } else {
        println!(
            "cargo:warning=Pre-built library not found.  Falling back \
             to from-source build"
        );
    }

    let mut finder = cmd_finder::CommandFinder::new();

    finder.must_have("cmake");
    finder.must_have("git");
    finder.must_have("python");

    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let shaderc_dir = Path::new(&manifest_dir).join("build");

    let mut lib_path = if target_env == "msvc" {
        finder.must_have("ninja");
        build_shaderc_msvc(&shaderc_dir)
    } else {
        let has_ninja = finder.maybe_have("ninja").is_some();
        build_shaderc(&shaderc_dir, has_ninja)
    };

    lib_path.push("lib");
    println!("cargo:rustc-link-search=native={}", lib_path.display());
    println!("cargo:rustc-link-lib=static=shaderc_combined");

    emit_std_cpp_link();
}

fn emit_std_cpp_link() {
    let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
    let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap();

    match (target_os.as_str(), target_env.as_str()) {
        ("linux", _) | ("windows", "gnu") => println!("cargo:rustc-link-lib=dylib=stdc++"),
        ("macos", _) => println!("cargo:rustc-link-lib=dylib=c++"),
        _ => {}
    }
}
<commit_msg>Add support for prebuilt msvc static libs (#60)<commit_after>// Copyright 2017 Google Inc.
//
// 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.

mod cmd_finder;

use std::env;
use std::env::consts;
use std::fs;
use std::path::{Path, PathBuf};

static COMBINED_LIB: &str = "shaderc_combined";
static DYNAMIC_LIB: &str = "shaderc_shared";
static COMBINED_LIB_FILE: &str = "libshaderc_combined.a";
static MSVC_COMBINED_LIB_FILE: &str = "shaderc_combined.lib";
static SPIRV_LIB_FILE: &str = "libSPIRV.a";

fn build_shaderc(shaderc_dir: &PathBuf, use_ninja: bool) -> PathBuf {
    let mut config = cmake::Config::new(shaderc_dir);
    config
        .profile("Release")
        .define("CMAKE_POSITION_INDEPENDENT_CODE", "ON")
        .define("SPIRV_SKIP_EXECUTABLES", "ON")
        .define("SPIRV_WERROR", "OFF")
        .define("SHADERC_SKIP_TESTS", "ON")
        .define("CMAKE_INSTALL_LIBDIR", "lib");
    if use_ninja {
        config.generator("Ninja");
    }
    config.build()
}

fn build_shaderc_msvc(shaderc_dir: &PathBuf) -> PathBuf {
    let mut config = cmake::Config::new(shaderc_dir);
    config
        .profile("Release")
        .define("CMAKE_POSITION_INDEPENDENT_CODE", "ON")
        .define("SPIRV_SKIP_EXECUTABLES", "ON")
        .define("SPIRV_WERROR", "OFF")
        .define("SHADERC_SKIP_TESTS", "ON")
        // cmake-rs tries to be clever on Windows by injecting several
        // C/C++ flags, which causes problems. So I have to manually
        // define CMAKE_*_FLAGS_* here to suppress that.
        .define("CMAKE_C_FLAGS", " /nologo /EHsc /MD")
        .define("CMAKE_CXX_FLAGS", " /nologo /EHsc /MD")
        .define("CMAKE_C_FLAGS_RELEASE", " /nologo /EHsc /MD")
        .define("CMAKE_CXX_FLAGS_RELEASE", " /nologo /EHsc /MD")

        // prevent shaderc's cmake script messes with crt flags
        .define("SHADERC_ENABLE_SHARED_CRT", "ON")
        .define("CMAKE_INSTALL_LIBDIR", "lib")
        .generator("Ninja");
    config.build()
}

fn main() {
    let config_build_from_source = env::var("CARGO_FEATURE_BUILD_FROM_SOURCE").is_ok();
    let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
    let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap();
    let explicit_lib_dir_set = env::var("SHADERC_LIB_DIR").is_ok();

    // Deprecated --no-defaults behavior from before shaderc-rs & shaderc-sys split
    // This block is only relevant to shaderc-rs but will skip all subsequent
    // build options
    if env::var("CARGO_FEATURE_CHECK_INVERTED_NO_DEFAULTS").is_ok()
        && env::var("CARGO_FEATURE_INVERTED_NO_DEFAULTS").is_err()
    {
        let out_dir = env::var("OUT_DIR").unwrap();
        println!("cargo:warning=USE OF --no-default-features IS DEPRECATED BEHAVIOR.");
        println!(
            "cargo:warning=Requested to use cargo out directory to find libshaderc_combined.a"
        );
        println!(
            "cargo:warning=Searching {} for shaderc_combined static lib...",
            out_dir
        );
        println!("cargo:rustc-link-search=native={}", out_dir);
        println!("cargo:rustc-link-lib=static=shaderc_combined");
        emit_std_cpp_link();
        return;
    }

    // Initialize explicit libshaderc search directory first
    let mut search_dir = if let Ok(lib_dir) = env::var("SHADERC_LIB_DIR") {
        println!(
            "cargo:warning=Specified {} to search for libshaderc.",
            lib_dir
        );
        Some(lib_dir)
    } else {
        None
    };

    // If no explicit path is set and no explicit request is made to build from
    // source, check known locations before falling back to from-source-build
    if search_dir.is_none() && target_os == "linux" && !config_build_from_source {
        println!(
            "cargo:warning=Checking for system installed libraries.  \
             Use --features = build-from-source to disable this behavior"
        );
        search_dir = Some("/usr/lib/".to_owned());
    }

    // Try to build with the static lib if a path was received or chosen
    let search_dir = if let Some(search_dir) = search_dir {
        let path = Path::new(&search_dir);
        let cannonical = fs::canonicalize(&path);
        if path.is_relative() {
            println!(
                "cargo:warning=Provided path {:?} is relative.  Path must be \
                 relative to shaderc-sys crate, possibly not your current \
                 working directory",
                &path
            );
        } else if path.is_dir() == false {
            println!(
                "cargo:warning=Provided path {:?} is not a directory.",
                &path
            );
        }
        if (cannonical.is_err()) && explicit_lib_dir_set {
            println!("cargo:warning={:?}", cannonical.err().unwrap());
            println!(
                "cargo:warning=Provided path {:?} could not be canonicallized",
                &path
            );
            None
        } else {
            cannonical.ok()
        }
    } else {
        None
    };

    if let Some(search_dir) = search_dir {
        let search_dir_str = search_dir.to_string_lossy();
        let combined_lib_path = search_dir.join(
            if target_os == "windows" && target_env == "msvc" {
                MSVC_COMBINED_LIB_FILE
            } else {
                COMBINED_LIB_FILE
            }
        );
        let dylib_name = format!("{}{}{}", consts::DLL_PREFIX, DYNAMIC_LIB, consts::DLL_SUFFIX);
        let dylib_path = search_dir.join(dylib_name.clone());

        if let Some((lib_dir, lib_name, kind)) = {
            if combined_lib_path.exists() {
                Some((&search_dir_str, COMBINED_LIB, "static"))
            } else if dylib_path.exists() {
                Some((&search_dir_str, DYNAMIC_LIB, "dylib"))
            } else {
                None
            }
        } {
            match (target_os.as_str(), target_env.as_str()) {
                ("linux", _) => {
                    println!("cargo:rustc-link-search=native={}", lib_dir);
                    let spirv_path = search_dir.join(SPIRV_LIB_FILE);
                    if spirv_path.exists() {
                        println!(
                            "cargo:warning=Found SPIRV.  Linking libSPIRV & \
                             libglslang"
                        );
                        println!("cargo:rustc-link-lib=static=SPIRV");
                        println!("cargo:rustc-link-lib=static=SPIRV-Tools-opt");
                        println!("cargo:rustc-link-lib=static=SPIRV-Tools");
                        println!("cargo:rustc-link-lib=glslang");
                    } else {
                        println!(
                            "cargo:warning=Only libshaderc library found.  \
                             Assuming libSPIRV & libglslang built into provided \
                             libshaderc."
                        );
                    }
                    println!("cargo:rustc-link-lib={}={}", kind, lib_name);
                    println!("cargo:rustc-link-lib=dylib=stdc++");
                    return;
                }
                ("windows", "msvc") => {
                    println!(
                        "cargo:warning=Windows MSVC static builds \
                         experimental"
                    );
                    println!("cargo:rustc-link-search=native={}", lib_dir);
                    println!("cargo:rustc-link-lib={}={}", kind, lib_name);
                    return;
                }
                ("windows", "gnu") => {
                    println!(
                        "cargo:warning=Windows MinGW static builds \
                         experimental"
                    );
                    println!("cargo:rustc-link-search=native={}", lib_dir);
                    println!("cargo:rustc-link-lib={}={}", kind, lib_name);
                    println!("cargo:rustc-link-lib=dylib=stdc++");
                    return;
                }
                ("macos", _) => {
                    println!("cargo:warning=MacOS static builds experimental");
                    println!("cargo:rustc-link-search=native={}", lib_dir);
                    println!("cargo:rustc-link-lib={}={}", kind, lib_name);
                    println!("cargo:rustc-link-lib=dylib=c++");
                    return;
                }
                (_, _) => {
                    println!(
                        "cargo:warning=Platform unsupported for pre-built \
                         libshaderc"
                    );
                }
            }
        }
    }

    if config_build_from_source {
        println!("cargo:warning=Requested to build from source");
    } else {
        println!(
            "cargo:warning=Pre-built library not found.  Falling back \
             to from-source build"
        );
    }

    let mut finder = cmd_finder::CommandFinder::new();

    finder.must_have("cmake");
    finder.must_have("git");
    finder.must_have("python");

    let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let shaderc_dir = Path::new(&manifest_dir).join("build");

    let mut lib_path = if target_env == "msvc" {
        finder.must_have("ninja");
        build_shaderc_msvc(&shaderc_dir)
    } else {
        let has_ninja = finder.maybe_have("ninja").is_some();
        build_shaderc(&shaderc_dir, has_ninja)
    };

    lib_path.push("lib");
    println!("cargo:rustc-link-search=native={}", lib_path.display());
    println!("cargo:rustc-link-lib=static=shaderc_combined");

    emit_std_cpp_link();
}

fn emit_std_cpp_link() {
    let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap();
    let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap();

    match (target_os.as_str(), target_env.as_str()) {
        ("linux", _) | ("windows", "gnu") => println!("cargo:rustc-link-lib=dylib=stdc++"),
        ("macos", _) => println!("cargo:rustc-link-lib=dylib=c++"),
        _ => {}
    }
}
<|endoftext|>


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

// Copyright (c) 2015 Rustcc Developers

//  Permission is hereby granted, free of charge, to any person obtaining a
//  copy of this software and associated documentation files (the "Software"),
//  to deal in the Software without restriction, including without limitation
//  the rights to use, copy, modify, merge, publish, distribute, sublicense,
//  and/or sell copies of the Software, and to permit persons to whom the
//  Software is furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
//  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
//  DEALINGS IN THE SOFTWARE.

use std::ptr;
use std::env::page_size;
use std::fmt;

use libc;

use mmap::{MemoryMap, MapOption};

/// A task's stack. The name "Stack" is a vestige of segmented stacks.
pub struct Stack {
    buf: Option<MemoryMap>,
    min_size: usize,
}

impl fmt::Debug for Stack {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(f, "Stack {} buf: ", "{"));
        match self.buf {
            Some(ref map) => try!(write!(f, "Some({:#x}), ", map.data() as libc::uintptr_t)),
            None => try!(write!(f, "None, ")),
        }
        write!(f, "min_size: {:?} {}", self.min_size, "}")
    }
}

// Try to use MAP_STACK on platforms that support it (it's what we're doing
// anyway), but some platforms don't support it at all. For example, it appears
// that there's a bug in freebsd that MAP_STACK implies MAP_FIXED (so it always
// fails): http://lists.freebsd.org/pipermail/freebsd-bugs/2011-July/044840.html
//
// DragonFly BSD also seems to suffer from the same problem. When MAP_STACK is
// used, it returns the same `ptr` multiple times.
#[cfg(all(not(windows), not(target_os = "freebsd"), not(target_os = "dragonfly")))]
static STACK_FLAGS: libc::c_int = libc::MAP_STACK | libc::MAP_PRIVATE | libc::MAP_ANON;
#[cfg(any(target_os = "freebsd",
          target_os = "dragonfly"))]
static STACK_FLAGS: libc::c_int = libc::MAP_PRIVATE | libc::MAP_ANON;
#[cfg(windows)]
static STACK_FLAGS: libc::c_int = 0;

impl Stack {
    /// Allocate a new stack of `size`. If size = 0, this will fail. Use
    /// `dummy_stack` if you want a zero-sized stack.
    pub fn new(size: usize) -> Stack {
        // Map in a stack. Eventually we might be able to handle stack
        // allocation failure, which would fail to spawn the task. But there's
        // not many sensible things to do on OOM.  Failure seems fine (and is
        // what the old stack allocation did).
        let stack = match MemoryMap::new(size, &[MapOption::MapReadable,
                                                 MapOption::MapWritable,
                                                 MapOption::MapNonStandardFlags(STACK_FLAGS)]) {
            Ok(map) => map,
            Err(e) => panic!("mmap for stack of size {} failed: {}", size, e)
        };

        // Change the last page to be inaccessible. This is to provide safety;
        // when an FFI function overflows it will (hopefully) hit this guard
        // page. It isn't guaranteed, but that's why FFI is unsafe. buf.data is
        // guaranteed to be aligned properly.
        if !protect_last_page(&stack) {
            panic!("Could not memory-protect guard page. stack={:?}",
                  stack.data());
        }

        Stack {
            buf: Some(stack),
            min_size: size,
        }
    }

    /// Create a 0-length stack which starts (and ends) at 0.
    #[allow(dead_code)]
    pub unsafe fn dummy_stack() -> Stack {
        Stack {
            buf: None,
            min_size: 0,
        }
    }

    #[allow(dead_code)]
    pub fn guard(&self) -> *const usize {
        (self.start() as usize + page_size()) as *const usize
    }

    /// Point to the low end of the allocated stack
    pub fn start(&self) -> *const usize {
        self.buf.as_ref()
            .map(|m| m.data() as *const usize)
            .unwrap_or(ptr::null())
    }

    /// Point one usize beyond the high end of the allocated stack
    pub fn end(&self) -> *const usize {
        self.buf
            .as_ref()
            .map(|buf| unsafe {
                buf.data().offset(buf.len() as isize) as *const usize
            })
            .unwrap_or(ptr::null())
    }

    /// Minimun size of this stack
    #[inline(always)]
    pub fn min_size(&self) -> usize {
        self.min_size
    }
}

#[cfg(unix)]
fn protect_last_page(stack: &MemoryMap) -> bool {
    unsafe {
        // This may seem backwards: the start of the segment is the last page?
        // Yes! The stack grows from higher addresses (the end of the allocated
        // block) to lower addresses (the start of the allocated block).
        let last_page = stack.data() as *mut libc::c_void;
        libc::mprotect(last_page, page_size() as libc::size_t,
                       libc::PROT_NONE) != -1
    }
}

#[cfg(windows)]
fn protect_last_page(stack: &MemoryMap) -> bool {
    unsafe {
        // see above
        let last_page = stack.data() as *mut libc::c_void;
        let mut old_prot: libc::DWORD = 0;
        libc::VirtualProtect(last_page, page_size() as libc::SIZE_T,
                             libc::PAGE_NOACCESS,
                             &mut old_prot as libc::LPDWORD) != 0
    }
}
<commit_msg>add back the new line<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//
// The MIT License (MIT)

// Copyright (c) 2015 Rustcc Developers

//  Permission is hereby granted, free of charge, to any person obtaining a
//  copy of this software and associated documentation files (the "Software"),
//  to deal in the Software without restriction, including without limitation
//  the rights to use, copy, modify, merge, publish, distribute, sublicense,
//  and/or sell copies of the Software, and to permit persons to whom the
//  Software is furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
//  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
//  DEALINGS IN THE SOFTWARE.

use std::ptr;
use std::env::page_size;
use std::fmt;

use libc;

use mmap::{MemoryMap, MapOption};

/// A task's stack. The name "Stack" is a vestige of segmented stacks.
pub struct Stack {
    buf: Option<MemoryMap>,
    min_size: usize,
}

impl fmt::Debug for Stack {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(f, "Stack {} buf: ", "{"));
        match self.buf {
            Some(ref map) => try!(write!(f, "Some({:#x}), ", map.data() as libc::uintptr_t)),
            None => try!(write!(f, "None, ")),
        }
        write!(f, "min_size: {:?} {}", self.min_size, "}")
    }
}

// Try to use MAP_STACK on platforms that support it (it's what we're doing
// anyway), but some platforms don't support it at all. For example, it appears
// that there's a bug in freebsd that MAP_STACK implies MAP_FIXED (so it always
// fails): http://lists.freebsd.org/pipermail/freebsd-bugs/2011-July/044840.html
//
// DragonFly BSD also seems to suffer from the same problem. When MAP_STACK is
// used, it returns the same `ptr` multiple times.
#[cfg(all(not(windows), not(target_os = "freebsd"), not(target_os = "dragonfly")))]
static STACK_FLAGS: libc::c_int = libc::MAP_STACK | libc::MAP_PRIVATE | libc::MAP_ANON;
#[cfg(any(target_os = "freebsd",
          target_os = "dragonfly"))]
static STACK_FLAGS: libc::c_int = libc::MAP_PRIVATE | libc::MAP_ANON;
#[cfg(windows)]
static STACK_FLAGS: libc::c_int = 0;

impl Stack {
    /// Allocate a new stack of `size`. If size = 0, this will fail. Use
    /// `dummy_stack` if you want a zero-sized stack.
    pub fn new(size: usize) -> Stack {
        // Map in a stack. Eventually we might be able to handle stack
        // allocation failure, which would fail to spawn the task. But there's
        // not many sensible things to do on OOM.  Failure seems fine (and is
        // what the old stack allocation did).
        let stack = match MemoryMap::new(size, &[MapOption::MapReadable,
                                                 MapOption::MapWritable,
                                                 MapOption::MapNonStandardFlags(STACK_FLAGS)]) {
            Ok(map) => map,
            Err(e) => panic!("mmap for stack of size {} failed: {}", size, e)
        };

        // Change the last page to be inaccessible. This is to provide safety;
        // when an FFI function overflows it will (hopefully) hit this guard
        // page. It isn't guaranteed, but that's why FFI is unsafe. buf.data is
        // guaranteed to be aligned properly.
        if !protect_last_page(&stack) {
            panic!("Could not memory-protect guard page. stack={:?}",
                  stack.data());
        }

        Stack {
            buf: Some(stack),
            min_size: size,
        }
    }

    /// Create a 0-length stack which starts (and ends) at 0.
    #[allow(dead_code)]
    pub unsafe fn dummy_stack() -> Stack {
        Stack {
            buf: None,
            min_size: 0,
        }
    }

    #[allow(dead_code)]
    pub fn guard(&self) -> *const usize {
        (self.start() as usize + page_size()) as *const usize
    }

    /// Point to the low end of the allocated stack
    pub fn start(&self) -> *const usize {
        self.buf.as_ref()
            .map(|m| m.data() as *const usize)
            .unwrap_or(ptr::null())
    }

    /// Point one usize beyond the high end of the allocated stack
    pub fn end(&self) -> *const usize {
        self.buf
            .as_ref()
            .map(|buf| unsafe {
                buf.data().offset(buf.len() as isize) as *const usize
            })
            .unwrap_or(ptr::null())
    }

    /// Minimun size of this stack
    #[inline(always)]
    pub fn min_size(&self) -> usize {
        self.min_size
    }
}

#[cfg(unix)]
fn protect_last_page(stack: &MemoryMap) -> bool {
    unsafe {
        // This may seem backwards: the start of the segment is the last page?
        // Yes! The stack grows from higher addresses (the end of the allocated
        // block) to lower addresses (the start of the allocated block).
        let last_page = stack.data() as *mut libc::c_void;
        libc::mprotect(last_page, page_size() as libc::size_t,
                       libc::PROT_NONE) != -1
    }
}

#[cfg(windows)]
fn protect_last_page(stack: &MemoryMap) -> bool {
    unsafe {
        // see above
        let last_page = stack.data() as *mut libc::c_void;
        let mut old_prot: libc::DWORD = 0;
        libc::VirtualProtect(last_page, page_size() as libc::SIZE_T,
                             libc::PAGE_NOACCESS,
                             &mut old_prot as libc::LPDWORD) != 0
    }
}
<|endoftext|>


<commit_before>#![crate_id(name="cut", vers="1.0.0", author="Rolf Morel")]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Rolf Morel <rolfmorel@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

#![feature(macro_rules)]

extern crate getopts;
extern crate libc;

use std::os;
use std::io::{print,File,BufferedWriter,BufferedReader,stdin};
use getopts::{optopt, optflag, getopts, usage};

use ranges::Range;

#[path = "../common/util.rs"]
mod util;
mod ranges;

static NAME: &'static str = "cut";
static VERSION: &'static str = "1.0.0";

struct Options {
    out_delim: Option<String>,
}

struct FieldOptions {
    delimiter: char,
    out_delimeter: String,
    only_delimited: bool,
}

enum Mode {
    Bytes(Vec<Range>, Options),
    Characters(Vec<Range>, Options),
    Fields(Vec<Range>, FieldOptions),
}

fn list_to_ranges(list: &str, complement: bool) -> Result<Vec<Range>, String> {
    use std::uint;

    let mut range_vec = {
        try!(
            if complement {
                Range::from_list(list).map(|r| ranges::complement(&r))
            } else {
                Range::from_list(list)
            }
        )
    };

    // add sentinel value for increased performance during cutting
    range_vec.push(Range{ low: uint::MAX, high: uint::MAX });

    Ok(range_vec)
}

fn cut_bytes<T: Reader>(mut reader: BufferedReader<T>,
                        ranges: &Vec<Range>,
                        opts: &Options) -> int {
    let mut out = BufferedWriter::new(std::io::stdio::stdout_raw());
    let (use_delim, out_delim) = match opts.out_delim.clone() {
        Some(delim) => (true, delim),
        None => (false, "".to_str())
    };

    let mut byte_pos = 0;
    let mut print_delim = false;
    let mut range_pos = 0;

    loop {
        let mut byte = [0u8];
        match reader.read(byte) {
            Ok(1) => (),
            Err(std::io::IoError{ kind: std::io::EndOfFile, ..}) => {
                if byte_pos > 0 {
                    out.write_u8('\n' as u8).unwrap();
                }
                break
            }
            _ => fail!(),
        }
        let byte = byte[0];

        if byte == ('\n' as u8) {
            out.write_u8('\n' as u8).unwrap();
            byte_pos = 0;
            print_delim = false;
            range_pos = 0;
        } else {
            byte_pos += 1;

            if byte_pos > ranges.get(range_pos).high {
                range_pos += 1;
            }

            let cur_range = *ranges.get(range_pos);

            if byte_pos >= cur_range.low {
                if use_delim {
                    if print_delim && byte_pos == cur_range.low {
                        out.write_str(out_delim.as_slice()).unwrap();
                    }

                    print_delim = true;
                }

                out.write_u8(byte).unwrap();
            }
        }
    }

    0
}

fn cut_characters<T: Reader>(mut reader: BufferedReader<T>,
                             ranges: &Vec<Range>,
                             opts: &Options) -> int {
    let mut out = BufferedWriter::new(std::io::stdio::stdout_raw());
    let (use_delim, out_delim) = match opts.out_delim.clone() {
        Some(delim) => (true, delim),
        None => (false, "".to_str())
    };

    let mut char_pos = 0;
    let mut print_delim = false;
    let mut range_pos = 0;

    loop {
        let character = match reader.read_char() {
            Ok(character) => character,
            Err(std::io::IoError{ kind: std::io::EndOfFile, ..}) => {
                if char_pos > 0 {
                    out.write_u8('\n' as u8).unwrap();
                }
                break
            }
            Err(std::io::IoError{ kind: std::io::InvalidInput, ..}) => {
                fail!("Invalid utf8");
            }
            _ => fail!(),
        };

        if character == '\n' {
            out.write_u8('\n' as u8).unwrap();
            char_pos = 0;
            print_delim = false;
            range_pos = 0;
        } else {
            char_pos += 1;

            if char_pos > ranges.get(range_pos).high {
                range_pos += 1;
            }

            let cur_range = *ranges.get(range_pos);

            if char_pos >= cur_range.low {
                if use_delim {
                    if print_delim && char_pos == cur_range.low {
                        out.write_str(out_delim.as_slice()).unwrap();
                    }

                    print_delim = true;
                }

                out.write_char(character).unwrap();
            }
        }
    }

    0
}

fn cut_fields<T: Reader>(reader: BufferedReader<T>,
                         ranges: &Vec<Range>,
                         opts: &FieldOptions) -> int {
    for range in ranges.iter() {
        println!("{}-{}", range.low, range.high);
    }

    0
}

fn cut_files(mut filenames: Vec<String>, mode: Mode) -> int {
    let mut stdin_read = false;
    let mut exit_code = 0;

    if filenames.len() == 0 { filenames.push("-".to_str()); }

    for filename in filenames.iter() {
        if filename.as_slice() == "-" {
            if stdin_read { continue; }

            exit_code |= match mode {
                Bytes(ref ranges, ref opts) => {
                    cut_bytes(stdin(), ranges, opts)
                }
                Characters(ref ranges, ref opts) => {
                    cut_characters(stdin(), ranges, opts)
                }
                Fields(ref ranges, ref opts) => {
                    cut_fields(stdin(), ranges, opts)
                }
            };

            stdin_read = true;
        } else {
            let path = Path::new(filename.as_slice());

            if ! path.exists() {
                show_error!("{}: No such file or directory", filename);
                continue;
            }

            let buf_file = match File::open(&path) {
                Ok(file) => BufferedReader::new(file),
                Err(e) => {
                    show_error!("{0:s}: {1:s}", filename.as_slice(),
                                e.desc.to_str());
                    continue
                }
            };

            exit_code |= match mode {
                Bytes(ref ranges, ref opts) => cut_bytes(buf_file, ranges, opts),
                Characters(ref ranges, ref opts) => {
                    cut_characters(buf_file, ranges, opts)
                }
                Fields(ref ranges, ref opts) => {
                    cut_fields(buf_file, ranges, opts)
                }
            };
        }
    }

    exit_code
}

#[allow(dead_code)]
fn main() { os::set_exit_status(uumain(os::args())); }

pub fn uumain(args: Vec<String>) -> int {
    let program = args.get(0).clone();
    let opts = [
        optopt("b", "bytes", "select only these bytes", "LIST"),
        optopt("c", "characters", "select only these characters", "LIST"),
        optopt("d", "delimiter", "use DELIM instead of TAB for field delimiter", "DELIM"),
        optopt("f", "fields", "select only these fields;  also print any line that contains no delimiter character, unless the -s option is specified", "LIST"),
        optflag("n", "", "(ignored)"),
        optflag("", "complement", "complement the set of selected bytes, characters or fields"),
        optflag("s", "only-delimited", "do not print lines not containing delimiters"),
        optopt("", "output-delimiter", "use STRING as the output delimiter the default is to use the input delimiter", "STRING"),
        optflag("", "help", "display this help and exit"),
        optflag("", "version", "output version information and exit"),
    ];

    let matches = match getopts(args.tail(), opts) {
        Ok(m) => m,
        Err(f) => {
            show_error!("Invalid options\n{}", f.to_err_msg())
            return 1;
        }
    };

    if matches.opt_present("help") {
        println!("Usage:");
        println!("  {0:s} OPTION... [FILE]...", program);
        println!("");
        print(usage("Print selected parts of lines from each FILE to standard output.", opts).as_slice());
        println!("");
        println!("Use one, and only one of -b, -c or -f.  Each LIST is made up of one");
        println!("range, or many ranges separated by commas.  Selected input is written");
        println!("in the same order that it is read, and is written exactly once.");
        println!("Each range is one of:");
        println!("");
        println!("  N     N'th byte, character or field, counted from 1");
        println!("  N-    from N'th byte, character or field, to end of line");
        println!("  N-M   from N'th to M'th (included) byte, character or field");
        println!("  -M    from first to M'th (included) byte, character or field");
        println!("");
        println!("With no FILE, or when FILE is -, read standard input.");
        return 0;
    }

    if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
        return 0;
    }

    let complement = matches.opt_present("complement");

    let mode_parse = match (matches.opt_str("bytes"),
                            matches.opt_str("characters"),
                            matches.opt_str("fields")) {
        (Some(byte_ranges), None, None) => {
            list_to_ranges(byte_ranges.as_slice(), complement).map(|ranges|
                Bytes(ranges,
                      Options{ out_delim: matches.opt_str("output-delimiter") })
            )
        }
        (None ,Some(char_ranges), None) => {
            list_to_ranges(char_ranges.as_slice(), complement).map(|ranges|
                Characters(ranges,
                           Options{ out_delim: matches.opt_str("output-delimiter") })
            )
        }
        (None, None ,Some(field_ranges)) => {
            list_to_ranges(field_ranges.as_slice(), complement).map(|ranges|
                {
                    use std::str::from_char;

                    let delim = matches.opt_str("delimiter")
                                       .filtered(|s| s.len() == 1)
                                       .map(|s| s.as_slice().char_at(0))
                                       .unwrap_or('\t');
                    let out_delim = matches.opt_str("output-delimiter")
                                           .unwrap_or(from_char(delim));
                    let only_delimited = matches.opt_present("only-delimited");

                    Fields(ranges,
                           FieldOptions{ delimiter: delim,
                                         out_delimeter: out_delim,
                                         only_delimited: only_delimited })
                }
            )
        }
        (ref b, ref c, ref f) if b.is_some() || c.is_some() || f.is_some() => {
            Err("only one type of list may be specified".to_str())
        }
        _ => Err("you must specify a list of bytes, characters, or fields".to_str())
    };

    match mode_parse {
        Ok(mode) => cut_files(matches.free, mode),
        Err(err_msg) => {
            show_error!("{}", err_msg);
            1
        }
    }
}
<commit_msg>Rewrite cut_bytes()<commit_after>#![crate_id(name="cut", vers="1.0.0", author="Rolf Morel")]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Rolf Morel <rolfmorel@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

#![feature(macro_rules)]

extern crate getopts;
extern crate libc;

use std::os;
use std::io::{print,File,BufferedWriter,BufferedReader,stdin};
use getopts::{optopt, optflag, getopts, usage};

use ranges::Range;

#[path = "../common/util.rs"]
mod util;
mod ranges;

static NAME: &'static str = "cut";
static VERSION: &'static str = "1.0.0";

struct Options {
    out_delim: Option<String>,
}

struct FieldOptions {
    delimiter: char,
    out_delimeter: String,
    only_delimited: bool,
}

enum Mode {
    Bytes(Vec<Range>, Options),
    Characters(Vec<Range>, Options),
    Fields(Vec<Range>, FieldOptions),
}

fn list_to_ranges(list: &str, complement: bool) -> Result<Vec<Range>, String> {
    use std::uint;

    let mut range_vec = {
        try!(
            if complement {
                Range::from_list(list).map(|r| ranges::complement(&r))
            } else {
                Range::from_list(list)
            }
        )
    };

    // add sentinel value for increased performance during cutting
    range_vec.push(Range{ low: uint::MAX, high: uint::MAX });

    Ok(range_vec)
}

fn cut_bytes<T: Reader>(mut reader: BufferedReader<T>,
                        ranges: &Vec<Range>,
                        opts: &Options) -> int {
    let mut out = BufferedWriter::new(std::io::stdio::stdout_raw());
    let (use_delim, out_delim) = match opts.out_delim.clone() {
        Some(delim) => (true, delim),
        None => (false, "".to_str())
    };

    'newline: loop {
        let line = match reader.read_until(b'\n') {
            Ok(line) => line,
            Err(std::io::IoError{ kind: std::io::EndOfFile, ..}) => break,
            _ => fail!(),
        };

        let line_len = line.len();
        let mut print_delim = false;

        for &Range{ low: low, high: high } in ranges.iter() {
            if low > line_len { break; }

            if use_delim {
                if print_delim {
                    out.write_str(out_delim.as_slice());
                }
                print_delim = true;
            }

            if high >= line_len {
                let segment = line.slice(low - 1, line_len);

                out.write(segment);

                if *line.get(line_len - 1) == b'\n' {
                    continue 'newline
                }
            } else {
                let segment = line.slice(low - 1, high);

                out.write(segment);
            }
        }

        out.write(&[b'\n']);
    }

    0
}

fn cut_characters<T: Reader>(mut reader: BufferedReader<T>,
                             ranges: &Vec<Range>,
                             opts: &Options) -> int {
    let mut out = BufferedWriter::new(std::io::stdio::stdout_raw());
    let (use_delim, out_delim) = match opts.out_delim.clone() {
        Some(delim) => (true, delim),
        None => (false, "".to_str())
    };

    let mut char_pos = 0;
    let mut print_delim = false;
    let mut range_pos = 0;

    loop {
        let character = match reader.read_char() {
            Ok(character) => character,
            Err(std::io::IoError{ kind: std::io::EndOfFile, ..}) => {
                if char_pos > 0 {
                    out.write_u8('\n' as u8).unwrap();
                }
                break
            }
            Err(std::io::IoError{ kind: std::io::InvalidInput, ..}) => {
                fail!("Invalid utf8");
            }
            _ => fail!(),
        };

        if character == '\n' {
            out.write_u8('\n' as u8).unwrap();
            char_pos = 0;
            print_delim = false;
            range_pos = 0;
        } else {
            char_pos += 1;

            if char_pos > ranges.get(range_pos).high {
                range_pos += 1;
            }

            let cur_range = *ranges.get(range_pos);

            if char_pos >= cur_range.low {
                if use_delim {
                    if print_delim && char_pos == cur_range.low {
                        out.write_str(out_delim.as_slice()).unwrap();
                    }

                    print_delim = true;
                }

                out.write_char(character).unwrap();
            }
        }
    }

    0
}

fn cut_fields<T: Reader>(reader: BufferedReader<T>,
                         ranges: &Vec<Range>,
                         opts: &FieldOptions) -> int {
    for range in ranges.iter() {
        println!("{}-{}", range.low, range.high);
    }

    0
}

fn cut_files(mut filenames: Vec<String>, mode: Mode) -> int {
    let mut stdin_read = false;
    let mut exit_code = 0;

    if filenames.len() == 0 { filenames.push("-".to_str()); }

    for filename in filenames.iter() {
        if filename.as_slice() == "-" {
            if stdin_read { continue; }

            exit_code |= match mode {
                Bytes(ref ranges, ref opts) => {
                    cut_bytes(stdin(), ranges, opts)
                }
                Characters(ref ranges, ref opts) => {
                    cut_characters(stdin(), ranges, opts)
                }
                Fields(ref ranges, ref opts) => {
                    cut_fields(stdin(), ranges, opts)
                }
            };

            stdin_read = true;
        } else {
            let path = Path::new(filename.as_slice());

            if ! path.exists() {
                show_error!("{}: No such file or directory", filename);
                continue;
            }

            let buf_file = match File::open(&path) {
                Ok(file) => BufferedReader::new(file),
                Err(e) => {
                    show_error!("{0:s}: {1:s}", filename.as_slice(),
                                e.desc.to_str());
                    continue
                }
            };

            exit_code |= match mode {
                Bytes(ref ranges, ref opts) => cut_bytes(buf_file, ranges, opts),
                Characters(ref ranges, ref opts) => {
                    cut_characters(buf_file, ranges, opts)
                }
                Fields(ref ranges, ref opts) => {
                    cut_fields(buf_file, ranges, opts)
                }
            };
        }
    }

    exit_code
}

#[allow(dead_code)]
fn main() { os::set_exit_status(uumain(os::args())); }

pub fn uumain(args: Vec<String>) -> int {
    let program = args.get(0).clone();
    let opts = [
        optopt("b", "bytes", "select only these bytes", "LIST"),
        optopt("c", "characters", "select only these characters", "LIST"),
        optopt("d", "delimiter", "use DELIM instead of TAB for field delimiter", "DELIM"),
        optopt("f", "fields", "select only these fields;  also print any line that contains no delimiter character, unless the -s option is specified", "LIST"),
        optflag("n", "", "(ignored)"),
        optflag("", "complement", "complement the set of selected bytes, characters or fields"),
        optflag("s", "only-delimited", "do not print lines not containing delimiters"),
        optopt("", "output-delimiter", "use STRING as the output delimiter the default is to use the input delimiter", "STRING"),
        optflag("", "help", "display this help and exit"),
        optflag("", "version", "output version information and exit"),
    ];

    let matches = match getopts(args.tail(), opts) {
        Ok(m) => m,
        Err(f) => {
            show_error!("Invalid options\n{}", f.to_err_msg())
            return 1;
        }
    };

    if matches.opt_present("help") {
        println!("Usage:");
        println!("  {0:s} OPTION... [FILE]...", program);
        println!("");
        print(usage("Print selected parts of lines from each FILE to standard output.", opts).as_slice());
        println!("");
        println!("Use one, and only one of -b, -c or -f.  Each LIST is made up of one");
        println!("range, or many ranges separated by commas.  Selected input is written");
        println!("in the same order that it is read, and is written exactly once.");
        println!("Each range is one of:");
        println!("");
        println!("  N     N'th byte, character or field, counted from 1");
        println!("  N-    from N'th byte, character or field, to end of line");
        println!("  N-M   from N'th to M'th (included) byte, character or field");
        println!("  -M    from first to M'th (included) byte, character or field");
        println!("");
        println!("With no FILE, or when FILE is -, read standard input.");
        return 0;
    }

    if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
        return 0;
    }

    let complement = matches.opt_present("complement");

    let mode_parse = match (matches.opt_str("bytes"),
                            matches.opt_str("characters"),
                            matches.opt_str("fields")) {
        (Some(byte_ranges), None, None) => {
            list_to_ranges(byte_ranges.as_slice(), complement).map(|ranges|
                Bytes(ranges,
                      Options{ out_delim: matches.opt_str("output-delimiter") })
            )
        }
        (None ,Some(char_ranges), None) => {
            list_to_ranges(char_ranges.as_slice(), complement).map(|ranges|
                Characters(ranges,
                           Options{ out_delim: matches.opt_str("output-delimiter") })
            )
        }
        (None, None ,Some(field_ranges)) => {
            list_to_ranges(field_ranges.as_slice(), complement).map(|ranges|
                {
                    use std::str::from_char;

                    let delim = matches.opt_str("delimiter")
                                       .filtered(|s| s.len() == 1)
                                       .map(|s| s.as_slice().char_at(0))
                                       .unwrap_or('\t');
                    let out_delim = matches.opt_str("output-delimiter")
                                           .unwrap_or(from_char(delim));
                    let only_delimited = matches.opt_present("only-delimited");

                    Fields(ranges,
                           FieldOptions{ delimiter: delim,
                                         out_delimeter: out_delim,
                                         only_delimited: only_delimited })
                }
            )
        }
        (ref b, ref c, ref f) if b.is_some() || c.is_some() || f.is_some() => {
            Err("only one type of list may be specified".to_str())
        }
        _ => Err("you must specify a list of bytes, characters, or fields".to_str())
    };

    match mode_parse {
        Ok(mode) => cut_files(matches.free, mode),
        Err(err_msg) => {
            show_error!("{}", err_msg);
            1
        }
    }
}
<|endoftext|>


<commit_before>/*!

# typeck

The type checker is responsible for:

1. Determining the type of each expression.
2. Resolving methods and traits.
3. Guaranteeing that most type rules are met. ("Most?", you say, "why most?"
   Well, dear reader, read on)

The main entry point is `check_crate()`. Type checking operates in
several major phases:

1. The collect phase first passes over all items and determines their
   type, without examining their "innards".

2. Variance inference then runs to compute the variance of each parameter.

3. Coherence checks for overlapping or orphaned impls.

4. Finally, the check phase then checks function bodies and so forth.
   Within the check phase, we check each function body one at a time
   (bodies of function expressions are checked as part of the
   containing function).  Inference is used to supply types wherever
   they are unknown. The actual checking of a function itself has
   several phases (check, regionck, writeback), as discussed in the
   documentation for the `check` module.

The type checker is defined into various submodules which are documented
independently:

- astconv: converts the AST representation of types
  into the `ty` representation.

- collect: computes the types of each top-level item and enters them into
  the `tcx.types` table for later use.

- coherence: enforces coherence rules, builds some tables.

- variance: variance inference

- outlives: outlives inference

- check: walks over function bodies and type checks them, inferring types for
  local variables, type parameters, etc as necessary.

- infer: finds the types to use for each type variable such that
  all subtyping and assignment constraints are met.  In essence, the check
  module specifies the constraints, and the infer module solves them.

## Note

This API is completely unstable and subject to change.

*/

#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
#![allow(non_camel_case_types)]
#![feature(bool_to_option)]
#![feature(box_syntax)]
#![feature(crate_visibility_modifier)]
#![feature(in_band_lifetimes)]
#![feature(nll)]
#![feature(or_patterns)]
#![feature(try_blocks)]
#![feature(never_type)]
#![feature(slice_partition_dedup)]
#![recursion_limit = "256"]

#[macro_use]
extern crate log;

#[macro_use]
extern crate rustc_middle;

// These are used by Clippy.
pub mod expr_use_visitor;
pub mod check;

mod astconv;
mod check_unused;
mod coherence;
mod collect;
mod constrained_generic_params;
mod impl_wf_check;
mod mem_categorization;
mod outlives;
mod structured_errors;
mod variance;

use rustc_errors::{struct_span_err, ErrorReported};
use rustc_hir as hir;
use rustc_hir::def_id::{LocalDefId, LOCAL_CRATE};
use rustc_hir::Node;
use rustc_infer::infer::{InferOk, TyCtxtInferExt};
use rustc_infer::traits::TraitEngineExt as _;
use rustc_middle::middle;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::util;
use rustc_session::config::EntryFnType;
use rustc_span::{Span, DUMMY_SP};
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
use rustc_trait_selection::traits::{
    ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt as _,
};

use std::iter;

use astconv::{AstConv, Bounds};

fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: Abi, span: Span) {
    if decl.c_variadic && !(abi == Abi::C || abi == Abi::Cdecl) {
        let mut err = struct_span_err!(
            tcx.sess,
            span,
            E0045,
            "C-variadic function must have C or cdecl calling convention"
        );
        err.span_label(span, "C-variadics require C or cdecl calling convention").emit();
    }
}

fn require_same_types<'tcx>(
    tcx: TyCtxt<'tcx>,
    cause: &ObligationCause<'tcx>,
    expected: Ty<'tcx>,
    actual: Ty<'tcx>,
) -> bool {
    tcx.infer_ctxt().enter(|ref infcx| {
        let param_env = ty::ParamEnv::empty();
        let mut fulfill_cx = TraitEngine::new(infcx.tcx);
        match infcx.at(&cause, param_env).eq(expected, actual) {
            Ok(InferOk { obligations, .. }) => {
                fulfill_cx.register_predicate_obligations(infcx, obligations);
            }
            Err(err) => {
                infcx.report_mismatched_types(cause, expected, actual, err).emit();
                return false;
            }
        }

        match fulfill_cx.select_all_or_error(infcx) {
            Ok(()) => true,
            Err(errors) => {
                infcx.report_fulfillment_errors(&errors, None, false);
                false
            }
        }
    })
}

fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: LocalDefId) {
    let main_id = tcx.hir().as_local_hir_id(main_def_id);
    let main_span = tcx.def_span(main_def_id);
    let main_t = tcx.type_of(main_def_id);
    match main_t.kind {
        ty::FnDef(..) => {
            if let Some(Node::Item(it)) = tcx.hir().find(main_id) {
                if let hir::ItemKind::Fn(ref sig, ref generics, _) = it.kind {
                    let mut error = false;
                    if !generics.params.is_empty() {
                        let msg = "`main` function is not allowed to have generic \
                                   parameters"
                            .to_owned();
                        let label = "`main` cannot have generic parameters".to_string();
                        struct_span_err!(tcx.sess, generics.span, E0131, "{}", msg)
                            .span_label(generics.span, label)
                            .emit();
                        error = true;
                    }
                    if let Some(sp) = generics.where_clause.span() {
                        struct_span_err!(
                            tcx.sess,
                            sp,
                            E0646,
                            "`main` function is not allowed to have a `where` clause"
                        )
                        .span_label(sp, "`main` cannot have a `where` clause")
                        .emit();
                        error = true;
                    }
                    if let hir::IsAsync::Async = sig.header.asyncness {
                        let span = tcx.sess.source_map().guess_head_span(it.span);
                        struct_span_err!(
                            tcx.sess,
                            span,
                            E0752,
                            "`main` function is not allowed to be `async`"
                        )
                        .span_label(span, "`main` function is not allowed to be `async`")
                        .emit();
                        error = true;
                    }
                    if error {
                        return;
                    }
                }
            }

            let actual = tcx.fn_sig(main_def_id);
            let expected_return_type = if tcx.lang_items().termination().is_some() {
                // we take the return type of the given main function, the real check is done
                // in `check_fn`
                actual.output().skip_binder()
            } else {
                // standard () main return type
                tcx.mk_unit()
            };

            let se_ty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig(
                iter::empty(),
                expected_return_type,
                false,
                hir::Unsafety::Normal,
                Abi::Rust,
            )));

            require_same_types(
                tcx,
                &ObligationCause::new(main_span, main_id, ObligationCauseCode::MainFunctionType),
                se_ty,
                tcx.mk_fn_ptr(actual),
            );
        }
        _ => {
            span_bug!(main_span, "main has a non-function type: found `{}`", main_t);
        }
    }
}

fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: LocalDefId) {
    let start_id = tcx.hir().as_local_hir_id(start_def_id);
    let start_span = tcx.def_span(start_def_id);
    let start_t = tcx.type_of(start_def_id);
    match start_t.kind {
        ty::FnDef(..) => {
            if let Some(Node::Item(it)) = tcx.hir().find(start_id) {
                if let hir::ItemKind::Fn(ref sig, ref generics, _) = it.kind {
                    let mut error = false;
                    if !generics.params.is_empty() {
                        struct_span_err!(
                            tcx.sess,
                            generics.span,
                            E0132,
                            "start function is not allowed to have type parameters"
                        )
                        .span_label(generics.span, "start function cannot have type parameters")
                        .emit();
                        error = true;
                    }
                    if let Some(sp) = generics.where_clause.span() {
                        struct_span_err!(
                            tcx.sess,
                            sp,
                            E0647,
                            "start function is not allowed to have a `where` clause"
                        )
                        .span_label(sp, "start function cannot have a `where` clause")
                        .emit();
                        error = true;
                    }
                    if let hir::IsAsync::Async = sig.header.asyncness {
                        let span = tcx.sess.source_map().guess_head_span(it.span);
                        struct_span_err!(
                            tcx.sess,
                            span,
                            E0752,
                            "start is not allowed to be `async`"
                        )
                        .span_label(span, "start is not allowed to be `async`")
                        .emit();
                        error = true;
                    }
                    if error {
                        return;
                    }
                }
            }

            let se_ty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig(
                [tcx.types.isize, tcx.mk_imm_ptr(tcx.mk_imm_ptr(tcx.types.u8))].iter().cloned(),
                tcx.types.isize,
                false,
                hir::Unsafety::Normal,
                Abi::Rust,
            )));

            require_same_types(
                tcx,
                &ObligationCause::new(start_span, start_id, ObligationCauseCode::StartFunctionType),
                se_ty,
                tcx.mk_fn_ptr(tcx.fn_sig(start_def_id)),
            );
        }
        _ => {
            span_bug!(start_span, "start has a non-function type: found `{}`", start_t);
        }
    }
}

fn check_for_entry_fn(tcx: TyCtxt<'_>) {
    match tcx.entry_fn(LOCAL_CRATE) {
        Some((def_id, EntryFnType::Main)) => check_main_fn_ty(tcx, def_id),
        Some((def_id, EntryFnType::Start)) => check_start_fn_ty(tcx, def_id),
        _ => {}
    }
}

pub fn provide(providers: &mut Providers) {
    collect::provide(providers);
    coherence::provide(providers);
    check::provide(providers);
    variance::provide(providers);
    outlives::provide(providers);
    impl_wf_check::provide(providers);
}

pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorReported> {
    let _prof_timer = tcx.sess.timer("type_check_crate");

    // this ensures that later parts of type checking can assume that items
    // have valid types and not error
    // FIXME(matthewjasper) We shouldn't need to do this.
    tcx.sess.track_errors(|| {
        tcx.sess.time("type_collecting", || {
            for &module in tcx.hir().krate().modules.keys() {
                tcx.ensure().collect_mod_item_types(tcx.hir().local_def_id(module));
            }
        });
    })?;

    if tcx.features().rustc_attrs {
        tcx.sess.track_errors(|| {
            tcx.sess.time("outlives_testing", || outlives::test::test_inferred_outlives(tcx));
        })?;
    }

    tcx.sess.track_errors(|| {
        tcx.sess.time("impl_wf_inference", || impl_wf_check::impl_wf_check(tcx));
    })?;

    tcx.sess.track_errors(|| {
        tcx.sess.time("coherence_checking", || coherence::check_coherence(tcx));
    })?;

    if tcx.features().rustc_attrs {
        tcx.sess.track_errors(|| {
            tcx.sess.time("variance_testing", || variance::test::test_variance(tcx));
        })?;
    }

    tcx.sess.track_errors(|| {
        tcx.sess.time("wf_checking", || check::check_wf_new(tcx));
    })?;

    tcx.sess.time("item_types_checking", || {
        for &module in tcx.hir().krate().modules.keys() {
            tcx.ensure().check_mod_item_types(tcx.hir().local_def_id(module));
        }
    });

    tcx.sess.time("item_bodies_checking", || tcx.typeck_item_bodies(LOCAL_CRATE));

    check_unused::check_crate(tcx);
    check_for_entry_fn(tcx);

    if tcx.sess.err_count() == 0 { Ok(()) } else { Err(ErrorReported) }
}

/// A quasi-deprecated helper used in rustdoc and clippy to get
/// the type from a HIR node.
pub fn hir_ty_to_ty<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: &hir::Ty<'_>) -> Ty<'tcx> {
    // In case there are any projections, etc., find the "environment"
    // def-ID that will be used to determine the traits/predicates in
    // scope.  This is derived from the enclosing item-like thing.
    let env_node_id = tcx.hir().get_parent_item(hir_ty.hir_id);
    let env_def_id = tcx.hir().local_def_id(env_node_id);
    let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id());

    astconv::AstConv::ast_ty_to_ty(&item_cx, hir_ty)
}

pub fn hir_trait_to_predicates<'tcx>(
    tcx: TyCtxt<'tcx>,
    hir_trait: &hir::TraitRef<'_>,
    self_ty: Ty<'tcx>,
) -> Bounds<'tcx> {
    // In case there are any projections, etc., find the "environment"
    // def-ID that will be used to determine the traits/predicates in
    // scope.  This is derived from the enclosing item-like thing.
    let env_hir_id = tcx.hir().get_parent_item(hir_trait.hir_ref_id);
    let env_def_id = tcx.hir().local_def_id(env_hir_id);
    let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id());
    let mut bounds = Bounds::default();
    let _ = AstConv::instantiate_poly_trait_ref_inner(
        &item_cx,
        hir_trait,
        DUMMY_SP,
        hir::Constness::NotConst,
        self_ty,
        &mut bounds,
        true,
    );

    bounds
}
<commit_msg>run ./x.py fmt<commit_after>/*!

# typeck

The type checker is responsible for:

1. Determining the type of each expression.
2. Resolving methods and traits.
3. Guaranteeing that most type rules are met. ("Most?", you say, "why most?"
   Well, dear reader, read on)

The main entry point is `check_crate()`. Type checking operates in
several major phases:

1. The collect phase first passes over all items and determines their
   type, without examining their "innards".

2. Variance inference then runs to compute the variance of each parameter.

3. Coherence checks for overlapping or orphaned impls.

4. Finally, the check phase then checks function bodies and so forth.
   Within the check phase, we check each function body one at a time
   (bodies of function expressions are checked as part of the
   containing function).  Inference is used to supply types wherever
   they are unknown. The actual checking of a function itself has
   several phases (check, regionck, writeback), as discussed in the
   documentation for the `check` module.

The type checker is defined into various submodules which are documented
independently:

- astconv: converts the AST representation of types
  into the `ty` representation.

- collect: computes the types of each top-level item and enters them into
  the `tcx.types` table for later use.

- coherence: enforces coherence rules, builds some tables.

- variance: variance inference

- outlives: outlives inference

- check: walks over function bodies and type checks them, inferring types for
  local variables, type parameters, etc as necessary.

- infer: finds the types to use for each type variable such that
  all subtyping and assignment constraints are met.  In essence, the check
  module specifies the constraints, and the infer module solves them.

## Note

This API is completely unstable and subject to change.

*/

#![doc(html_root_url = "https://doc.rust-lang.org/nightly/")]
#![allow(non_camel_case_types)]
#![feature(bool_to_option)]
#![feature(box_syntax)]
#![feature(crate_visibility_modifier)]
#![feature(in_band_lifetimes)]
#![feature(nll)]
#![feature(or_patterns)]
#![feature(try_blocks)]
#![feature(never_type)]
#![feature(slice_partition_dedup)]
#![recursion_limit = "256"]

#[macro_use]
extern crate log;

#[macro_use]
extern crate rustc_middle;

// These are used by Clippy.
pub mod check;
pub mod expr_use_visitor;

mod astconv;
mod check_unused;
mod coherence;
mod collect;
mod constrained_generic_params;
mod impl_wf_check;
mod mem_categorization;
mod outlives;
mod structured_errors;
mod variance;

use rustc_errors::{struct_span_err, ErrorReported};
use rustc_hir as hir;
use rustc_hir::def_id::{LocalDefId, LOCAL_CRATE};
use rustc_hir::Node;
use rustc_infer::infer::{InferOk, TyCtxtInferExt};
use rustc_infer::traits::TraitEngineExt as _;
use rustc_middle::middle;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::util;
use rustc_session::config::EntryFnType;
use rustc_span::{Span, DUMMY_SP};
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
use rustc_trait_selection::traits::{
    ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt as _,
};

use std::iter;

use astconv::{AstConv, Bounds};

fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: Abi, span: Span) {
    if decl.c_variadic && !(abi == Abi::C || abi == Abi::Cdecl) {
        let mut err = struct_span_err!(
            tcx.sess,
            span,
            E0045,
            "C-variadic function must have C or cdecl calling convention"
        );
        err.span_label(span, "C-variadics require C or cdecl calling convention").emit();
    }
}

fn require_same_types<'tcx>(
    tcx: TyCtxt<'tcx>,
    cause: &ObligationCause<'tcx>,
    expected: Ty<'tcx>,
    actual: Ty<'tcx>,
) -> bool {
    tcx.infer_ctxt().enter(|ref infcx| {
        let param_env = ty::ParamEnv::empty();
        let mut fulfill_cx = TraitEngine::new(infcx.tcx);
        match infcx.at(&cause, param_env).eq(expected, actual) {
            Ok(InferOk { obligations, .. }) => {
                fulfill_cx.register_predicate_obligations(infcx, obligations);
            }
            Err(err) => {
                infcx.report_mismatched_types(cause, expected, actual, err).emit();
                return false;
            }
        }

        match fulfill_cx.select_all_or_error(infcx) {
            Ok(()) => true,
            Err(errors) => {
                infcx.report_fulfillment_errors(&errors, None, false);
                false
            }
        }
    })
}

fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: LocalDefId) {
    let main_id = tcx.hir().as_local_hir_id(main_def_id);
    let main_span = tcx.def_span(main_def_id);
    let main_t = tcx.type_of(main_def_id);
    match main_t.kind {
        ty::FnDef(..) => {
            if let Some(Node::Item(it)) = tcx.hir().find(main_id) {
                if let hir::ItemKind::Fn(ref sig, ref generics, _) = it.kind {
                    let mut error = false;
                    if !generics.params.is_empty() {
                        let msg = "`main` function is not allowed to have generic \
                                   parameters"
                            .to_owned();
                        let label = "`main` cannot have generic parameters".to_string();
                        struct_span_err!(tcx.sess, generics.span, E0131, "{}", msg)
                            .span_label(generics.span, label)
                            .emit();
                        error = true;
                    }
                    if let Some(sp) = generics.where_clause.span() {
                        struct_span_err!(
                            tcx.sess,
                            sp,
                            E0646,
                            "`main` function is not allowed to have a `where` clause"
                        )
                        .span_label(sp, "`main` cannot have a `where` clause")
                        .emit();
                        error = true;
                    }
                    if let hir::IsAsync::Async = sig.header.asyncness {
                        let span = tcx.sess.source_map().guess_head_span(it.span);
                        struct_span_err!(
                            tcx.sess,
                            span,
                            E0752,
                            "`main` function is not allowed to be `async`"
                        )
                        .span_label(span, "`main` function is not allowed to be `async`")
                        .emit();
                        error = true;
                    }
                    if error {
                        return;
                    }
                }
            }

            let actual = tcx.fn_sig(main_def_id);
            let expected_return_type = if tcx.lang_items().termination().is_some() {
                // we take the return type of the given main function, the real check is done
                // in `check_fn`
                actual.output().skip_binder()
            } else {
                // standard () main return type
                tcx.mk_unit()
            };

            let se_ty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig(
                iter::empty(),
                expected_return_type,
                false,
                hir::Unsafety::Normal,
                Abi::Rust,
            )));

            require_same_types(
                tcx,
                &ObligationCause::new(main_span, main_id, ObligationCauseCode::MainFunctionType),
                se_ty,
                tcx.mk_fn_ptr(actual),
            );
        }
        _ => {
            span_bug!(main_span, "main has a non-function type: found `{}`", main_t);
        }
    }
}

fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: LocalDefId) {
    let start_id = tcx.hir().as_local_hir_id(start_def_id);
    let start_span = tcx.def_span(start_def_id);
    let start_t = tcx.type_of(start_def_id);
    match start_t.kind {
        ty::FnDef(..) => {
            if let Some(Node::Item(it)) = tcx.hir().find(start_id) {
                if let hir::ItemKind::Fn(ref sig, ref generics, _) = it.kind {
                    let mut error = false;
                    if !generics.params.is_empty() {
                        struct_span_err!(
                            tcx.sess,
                            generics.span,
                            E0132,
                            "start function is not allowed to have type parameters"
                        )
                        .span_label(generics.span, "start function cannot have type parameters")
                        .emit();
                        error = true;
                    }
                    if let Some(sp) = generics.where_clause.span() {
                        struct_span_err!(
                            tcx.sess,
                            sp,
                            E0647,
                            "start function is not allowed to have a `where` clause"
                        )
                        .span_label(sp, "start function cannot have a `where` clause")
                        .emit();
                        error = true;
                    }
                    if let hir::IsAsync::Async = sig.header.asyncness {
                        let span = tcx.sess.source_map().guess_head_span(it.span);
                        struct_span_err!(
                            tcx.sess,
                            span,
                            E0752,
                            "start is not allowed to be `async`"
                        )
                        .span_label(span, "start is not allowed to be `async`")
                        .emit();
                        error = true;
                    }
                    if error {
                        return;
                    }
                }
            }

            let se_ty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig(
                [tcx.types.isize, tcx.mk_imm_ptr(tcx.mk_imm_ptr(tcx.types.u8))].iter().cloned(),
                tcx.types.isize,
                false,
                hir::Unsafety::Normal,
                Abi::Rust,
            )));

            require_same_types(
                tcx,
                &ObligationCause::new(start_span, start_id, ObligationCauseCode::StartFunctionType),
                se_ty,
                tcx.mk_fn_ptr(tcx.fn_sig(start_def_id)),
            );
        }
        _ => {
            span_bug!(start_span, "start has a non-function type: found `{}`", start_t);
        }
    }
}

fn check_for_entry_fn(tcx: TyCtxt<'_>) {
    match tcx.entry_fn(LOCAL_CRATE) {
        Some((def_id, EntryFnType::Main)) => check_main_fn_ty(tcx, def_id),
        Some((def_id, EntryFnType::Start)) => check_start_fn_ty(tcx, def_id),
        _ => {}
    }
}

pub fn provide(providers: &mut Providers) {
    collect::provide(providers);
    coherence::provide(providers);
    check::provide(providers);
    variance::provide(providers);
    outlives::provide(providers);
    impl_wf_check::provide(providers);
}

pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorReported> {
    let _prof_timer = tcx.sess.timer("type_check_crate");

    // this ensures that later parts of type checking can assume that items
    // have valid types and not error
    // FIXME(matthewjasper) We shouldn't need to do this.
    tcx.sess.track_errors(|| {
        tcx.sess.time("type_collecting", || {
            for &module in tcx.hir().krate().modules.keys() {
                tcx.ensure().collect_mod_item_types(tcx.hir().local_def_id(module));
            }
        });
    })?;

    if tcx.features().rustc_attrs {
        tcx.sess.track_errors(|| {
            tcx.sess.time("outlives_testing", || outlives::test::test_inferred_outlives(tcx));
        })?;
    }

    tcx.sess.track_errors(|| {
        tcx.sess.time("impl_wf_inference", || impl_wf_check::impl_wf_check(tcx));
    })?;

    tcx.sess.track_errors(|| {
        tcx.sess.time("coherence_checking", || coherence::check_coherence(tcx));
    })?;

    if tcx.features().rustc_attrs {
        tcx.sess.track_errors(|| {
            tcx.sess.time("variance_testing", || variance::test::test_variance(tcx));
        })?;
    }

    tcx.sess.track_errors(|| {
        tcx.sess.time("wf_checking", || check::check_wf_new(tcx));
    })?;

    tcx.sess.time("item_types_checking", || {
        for &module in tcx.hir().krate().modules.keys() {
            tcx.ensure().check_mod_item_types(tcx.hir().local_def_id(module));
        }
    });

    tcx.sess.time("item_bodies_checking", || tcx.typeck_item_bodies(LOCAL_CRATE));

    check_unused::check_crate(tcx);
    check_for_entry_fn(tcx);

    if tcx.sess.err_count() == 0 { Ok(()) } else { Err(ErrorReported) }
}

/// A quasi-deprecated helper used in rustdoc and clippy to get
/// the type from a HIR node.
pub fn hir_ty_to_ty<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: &hir::Ty<'_>) -> Ty<'tcx> {
    // In case there are any projections, etc., find the "environment"
    // def-ID that will be used to determine the traits/predicates in
    // scope.  This is derived from the enclosing item-like thing.
    let env_node_id = tcx.hir().get_parent_item(hir_ty.hir_id);
    let env_def_id = tcx.hir().local_def_id(env_node_id);
    let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id());

    astconv::AstConv::ast_ty_to_ty(&item_cx, hir_ty)
}

pub fn hir_trait_to_predicates<'tcx>(
    tcx: TyCtxt<'tcx>,
    hir_trait: &hir::TraitRef<'_>,
    self_ty: Ty<'tcx>,
) -> Bounds<'tcx> {
    // In case there are any projections, etc., find the "environment"
    // def-ID that will be used to determine the traits/predicates in
    // scope.  This is derived from the enclosing item-like thing.
    let env_hir_id = tcx.hir().get_parent_item(hir_trait.hir_ref_id);
    let env_def_id = tcx.hir().local_def_id(env_hir_id);
    let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id());
    let mut bounds = Bounds::default();
    let _ = AstConv::instantiate_poly_trait_ref_inner(
        &item_cx,
        hir_trait,
        DUMMY_SP,
        hir::Constness::NotConst,
        self_ty,
        &mut bounds,
        true,
    );

    bounds
}
<|endoftext|>