<commit_before>extern crate limonite;

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

use std::mem::transmute;

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

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

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

    let mut llvm_generator = LLVMGenerator::new();

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

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

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

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

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

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

    let mut llvm_generator = LLVMGenerator::new();

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

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

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

    assert_eq!(inc_until(), 10);
}

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

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

    let mut llvm_generator = LLVMGenerator::new();

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

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

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

    assert_eq!(dec_until(), 0);
}

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

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

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

    let mut llvm_generator = LLVMGenerator::new();

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

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

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

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

    hello_world();
}
<commit_msg>Remove dump IR debug<commit_after>extern crate limonite;

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

use std::mem::transmute;

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

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

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

    let mut llvm_generator = LLVMGenerator::new();

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

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

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

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

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

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

    let mut llvm_generator = LLVMGenerator::new();

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

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

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

    assert_eq!(inc_until(), 10);
}

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

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

    let mut llvm_generator = LLVMGenerator::new();

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

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

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

    assert_eq!(dec_until(), 0);
}

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

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

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

    let mut llvm_generator = LLVMGenerator::new();

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

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

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

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

    hello_world();
}
<|endoftext|>


<commit_before>use bytes::Bytes;

use crate::requests::*;
use crate::types::*;

#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct InputFile(InputFileImpl);

#[derive(Debug, Clone, PartialEq, PartialOrd)]
enum InputFileImpl {
    Ref(Text),
    Path { path: Text, file_name: Option<Text> },
    Data { file_name: Text, data: Bytes },
}

impl InputFile {
    pub fn with_ref(r: impl Into<Text>) -> Self {
        InputFile(InputFileImpl::Ref(r.into()))
    }

    pub fn with_path(path: impl Into<Text>) -> Self {
        InputFile(InputFileImpl::Path {
            path: path.into(),
            file_name: None,
        })
    }

    pub fn with_path_and_name(path: impl Into<Text>, file_name: impl Into<Text>) -> Self {
        InputFile(InputFileImpl::Path {
            path: path.into(),
            file_name: Some(file_name.into()),
        })
    }

    pub fn with_data(data: impl Into<Bytes>, file_name: impl Into<Text>) -> Self {
        InputFile(InputFileImpl::Data {
            file_name: file_name.into(),
            data: data.into(),
        })
    }
}

impl From<FileRef> for InputFile {
    fn from(value: FileRef) -> Self {
        InputFile::with_ref(value.inner)
    }
}

impl<'a> From<&'a FileRef> for InputFile {
    fn from(value: &'a FileRef) -> Self {
        InputFile::with_ref(value.inner.as_str())
    }
}

impl ToMultipartValue for InputFile {
    fn to_multipart_value(&self) -> MultipartValue {
        match &self.0 {
            InputFileImpl::Ref(r) => MultipartValue::Text(r.clone()),
            InputFileImpl::Path { path, file_name } => MultipartValue::Path {
                path: path.clone(),
                file_name: file_name.clone(),
            },
            InputFileImpl::Data { file_name, data } => MultipartValue::Data {
                file_name: file_name.clone(),
                data: data.clone(),
            },
        }
    }
}
<commit_msg>refactor: remove InputFile::with_path_and_name method and add InputFile::file_name method<commit_after>use bytes::Bytes;

use crate::requests::*;
use crate::types::*;

#[derive(Debug, Clone, PartialEq, PartialOrd)]
pub struct InputFile(InputFileImpl);

#[derive(Debug, Clone, PartialEq, PartialOrd)]
enum InputFileImpl {
    Ref(Text),
    Path { path: Text, file_name: Option<Text> },
    Data { file_name: Text, data: Bytes },
}

impl InputFile {
    pub fn with_ref(r: impl Into<Text>) -> Self {
        InputFile(InputFileImpl::Ref(r.into()))
    }

    pub fn with_path(path: impl Into<Text>) -> Self {
        InputFile(InputFileImpl::Path {
            path: path.into(),
            file_name: None,
        })
    }

    pub fn with_data(data: impl Into<Bytes>, file_name: impl Into<Text>) -> Self {
        InputFile(InputFileImpl::Data {
            file_name: file_name.into(),
            data: data.into(),
        })
    }

    pub fn file_name(&mut self, new_file_name: impl Into<Text>) -> &mut Self {
        match &mut self.0 {
            InputFileImpl::Ref(_) => (),
            InputFileImpl::Path { file_name, .. } => *file_name = Some(new_file_name.into()),
            InputFileImpl::Data { file_name, .. } => *file_name = new_file_name.into(),
        };
        self
    }
}

impl From<FileRef> for InputFile {
    fn from(value: FileRef) -> Self {
        InputFile::with_ref(value.inner)
    }
}

impl<'a> From<&'a FileRef> for InputFile {
    fn from(value: &'a FileRef) -> Self {
        InputFile::with_ref(value.inner.as_str())
    }
}

impl<'a> From<&'a InputFile> for InputFile {
    fn from(value: &'a InputFile) -> Self {
        value.clone()
    }
}

impl<'a> From<&'a mut InputFile> for InputFile {
    fn from(value: &'a mut InputFile) -> Self {
        value.clone()
    }
}

impl ToMultipartValue for InputFile {
    fn to_multipart_value(&self) -> MultipartValue {
        match &self.0 {
            InputFileImpl::Ref(r) => MultipartValue::Text(r.clone()),
            InputFileImpl::Path { path, file_name } => MultipartValue::Path {
                path: path.clone(),
                file_name: file_name.clone(),
            },
            InputFileImpl::Data { file_name, data } => MultipartValue::Data {
                file_name: file_name.clone(),
                data: data.clone(),
            },
        }
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0.
 */

// snippet-start:[rust.example_code.s3.scenario_getting_started.bin]

// snippet-start:[rust.example_code.s3.basics.imports]

use aws_config::meta::region::RegionProviderChain;
use aws_sdk_s3::{Client, Error, Region};
use uuid::Uuid;

// snippet-end:[rust.example_code.s3.basics.imports]

// snippet-start:[rust.example_code.s3.basics.main]
#[tokio::main]
async fn main() -> Result<(), Error> {
    let (region, client, bucket_name, file_name, key, target_key) = initialize_variables().await;

    if let Err(e) = run_s3_operations(region, client, bucket_name, file_name, key, target_key).await
    {
        println!("{:?}", e);
    };

    Ok(())
}
// snippet-end:[rust.example_code.s3.basics.main]

// snippet-start:[rust.example_code.s3.basics.initialize_variables]
async fn initialize_variables() -> (Region, Client, String, String, String, String) {
    let region_provider = RegionProviderChain::first_try(Region::new("us-west-2"));
    let region = region_provider.region().await.unwrap();

    let shared_config = aws_config::from_env().region(region_provider).load().await;
    let client = Client::new(&shared_config);

    let bucket_name = format!("{}{}", "doc-example-bucket-", Uuid::new_v4().to_string());
    let file_name = "s3/testfile.txt".to_string();
    let key = "test file key name".to_string();
    let target_key = "target_key".to_string();

    (region, client, bucket_name, file_name, key, target_key)
}
// snippet-end:[rust.example_code.initialize_variables]

// snippet-start:[rust.example_code.s3.basics.run_s3_operations]
async fn run_s3_operations(
    region: Region,
    client: Client,
    bucket_name: String,
    file_name: String,
    key: String,
    target_key: String,
) -> Result<(), Error> {
    s3_service::create_bucket(&client, &bucket_name, region.as_ref()).await?;
    s3_service::upload_object(&client, &bucket_name, &file_name, &key).await?;
    s3_service::download_object(&client, &bucket_name, &key).await?;
    s3_service::copy_object(&client, &bucket_name, &key, &target_key).await?;
    s3_service::list_objects(&client, &bucket_name).await?;
    s3_service::delete_objects(&client, &bucket_name).await?;
    s3_service::delete_bucket(&client, &bucket_name).await?;

    Ok(())
}
// snippet-end:[rust.example_code.s3.basics.run_s3_operations]

// snippet-end:[rust.example_code.s3.scenario_getting_started.bin]
<commit_msg>Fixes incorrect end snippet.<commit_after>/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0.
 */

// snippet-start:[rust.example_code.s3.scenario_getting_started.bin]

// snippet-start:[rust.example_code.s3.basics.imports]

use aws_config::meta::region::RegionProviderChain;
use aws_sdk_s3::{Client, Error, Region};
use uuid::Uuid;

// snippet-end:[rust.example_code.s3.basics.imports]

// snippet-start:[rust.example_code.s3.basics.main]
#[tokio::main]
async fn main() -> Result<(), Error> {
    let (region, client, bucket_name, file_name, key, target_key) = initialize_variables().await;

    if let Err(e) = run_s3_operations(region, client, bucket_name, file_name, key, target_key).await
    {
        println!("{:?}", e);
    };

    Ok(())
}
// snippet-end:[rust.example_code.s3.basics.main]

// snippet-start:[rust.example_code.s3.basics.initialize_variables]
async fn initialize_variables() -> (Region, Client, String, String, String, String) {
    let region_provider = RegionProviderChain::first_try(Region::new("us-west-2"));
    let region = region_provider.region().await.unwrap();

    let shared_config = aws_config::from_env().region(region_provider).load().await;
    let client = Client::new(&shared_config);

    let bucket_name = format!("{}{}", "doc-example-bucket-", Uuid::new_v4().to_string());
    let file_name = "s3/testfile.txt".to_string();
    let key = "test file key name".to_string();
    let target_key = "target_key".to_string();

    (region, client, bucket_name, file_name, key, target_key)
}
// snippet-end:[rust.example_code.s3.basics.initialize_variables]

// snippet-start:[rust.example_code.s3.basics.run_s3_operations]
async fn run_s3_operations(
    region: Region,
    client: Client,
    bucket_name: String,
    file_name: String,
    key: String,
    target_key: String,
) -> Result<(), Error> {
    s3_service::create_bucket(&client, &bucket_name, region.as_ref()).await?;
    s3_service::upload_object(&client, &bucket_name, &file_name, &key).await?;
    s3_service::download_object(&client, &bucket_name, &key).await?;
    s3_service::copy_object(&client, &bucket_name, &key, &target_key).await?;
    s3_service::list_objects(&client, &bucket_name).await?;
    s3_service::delete_objects(&client, &bucket_name).await?;
    s3_service::delete_bucket(&client, &bucket_name).await?;

    Ok(())
}
// snippet-end:[rust.example_code.s3.basics.run_s3_operations]

// snippet-end:[rust.example_code.s3.scenario_getting_started.bin]
<|endoftext|>


<commit_before>//! Helper module which helps to determine amount of threads to be used
//! during tests execution.
use std::env;
use std::thread;

#[allow(deprecated)]
pub fn get_concurrency() -> usize {
    match env::var("RUST_TEST_THREADS") {
        Ok(s) => {
            let opt_n: Option<usize> = s.parse().ok();
            match opt_n {
                Some(n) if n > 0 => n,
                _ => panic!("RUST_TEST_THREADS is `{}`, should be a positive integer.", s),
            }
        }
        Err(..) => thread::available_concurrency().map(|n| n.get()).unwrap_or(1),
    }
}
<commit_msg>Rollup merge of #78065 - tshepang:nits, r=dtolnay<commit_after>//! Helper module which helps to determine amount of threads to be used
//! during tests execution.
use std::{env, num::NonZeroUsize, thread};

pub fn get_concurrency() -> usize {
    if let Ok(value) = env::var("RUST_TEST_THREADS") {
        match value.parse::<NonZeroUsize>().ok() {
            Some(n) => n.get(),
            _ => panic!("RUST_TEST_THREADS is `{}`, should be a positive integer.", value),
        }
    } else {
        thread::available_concurrency().map(|n| n.get()).unwrap_or(1)
    }
}
<|endoftext|>


<commit_before>#![crate_name = "rustful_macros"]

#![comment = "Macros for rustful"]
#![license = "MIT"]
#![crate_type = "dylib"]

#![doc(html_root_url = "http://ogeon.github.io/rustful/doc/")]

#![feature(macro_rules, plugin_registrar, quote, phase)]

//!This crate provides some helpful macros for rustful, including `router!` and `routes!`.
//!
//!#`router!`
//!The `router!` macro generates a `TreeRouter` containing the provided handlers and routing tree.
//!This can be useful to lower the risk of typing errors, among other things.
//!
//!##Example 1
//!
//!```rust ignore
//!#![feature(phase)]
//!#[phase(plugin)]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!
//!let router = router!{
//!	"/about" => Get: about_us,
//!	"/user/:user" => Get: show_user,
//!	"/product/:name" => Get: show_product,
//!	"/*" => Get: show_error,
//!	"/" => Get: show_welcome
//!};
//!```
//!
//!##Example 2
//!
//!```rust ignore
//!#![feature(phase)]
//!#[phase(plugin)]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!
//!let router = router!{
//!	"/" => Get: show_home,
//!	"home" => Get: show_home,
//!	"user/:username" => {Get: show_user, Post: save_user},
//!	"product" => {
//!		Get: show_all_products,
//!
//!		"json" => Get: send_all_product_data
//!		":id" => {
//!			Get: show_product,
//!			Post | Delete: edit_product,
//!
//!			"json" => Get: send_product_data
//!		}
//!	}
//!};
//!```
//!
//!#`routes!`
//!
//!The `routes!` macro generates a vector of routes, which can be used to create a `TreeRouter`.
//!It has the same syntax as `routes!`.
//!
//!```rust ignore
//!#![feature(phase)]
//!#[phase(plugin)]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!
//!let routes = routes!{
//!	"/" => Get: show_home,
//!	"home" => Get: show_home,
//!	"user/:username" => {Get: show_user, Post: save_user},
//!	"product" => {
//!		Get: show_all_products,
//!
//!		"json" => Get: send_all_product_data
//!		":id" => {
//!			Get: show_product,
//!			Post | Delete: edit_product,
//!
//!			"json" => Get: send_product_data
//!		}
//!	}
//!};
//!
//!let router = TreeRouter::from_routes(&routes);
//!```

extern crate syntax;
extern crate rustc;

use std::path::BytesContainer;

use syntax::{ast, codemap};
use syntax::ext::base::{
	ExtCtxt, MacResult, MacExpr,
	NormalTT, TTMacroExpander
};
use syntax::ext::build::AstBuilder;
use syntax::ext::quote::rt::ExtParseUtils;
use syntax::parse;
use syntax::parse::token;
use syntax::parse::parser;
use syntax::parse::parser::Parser;
use syntax::ptr::P;
//use syntax::print::pprust;

use rustc::plugin::Registry;

#[plugin_registrar]
#[doc(hidden)]
pub fn macro_registrar(reg: &mut Registry) {
	let expander = box expand_router as Box<TTMacroExpander>;
	reg.register_syntax_extension(token::intern("router"), NormalTT(expander, None));

	let expander = box expand_routes as Box<TTMacroExpander>;
	reg.register_syntax_extension(token::intern("routes"), NormalTT(expander, None));
}

fn expand_router<'cx>(cx: &'cx mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree]) -> Box<MacResult + 'cx> {
	let router_ident = cx.ident_of("router");
	let insert_method = cx.ident_of("insert_item");

	let mut calls: Vec<P<ast::Stmt>> = vec!(
		cx.stmt_let(sp, true, router_ident, quote_expr!(&cx, ::rustful::TreeRouter::new()))
	);

	for (path, method, handler) in parse_routes(cx, tts).into_iter() {
		let path_expr = cx.parse_expr(format!("\"{}\"", path));
		let method_expr = cx.expr_path(method);
		calls.push(cx.stmt_expr(
			cx.expr_method_call(sp, cx.expr_ident(sp, router_ident), insert_method, vec!(method_expr, path_expr, handler))
		));
	}

	let block = cx.expr_block(cx.block(sp, calls, Some(cx.expr_ident(sp, router_ident))));

	MacExpr::new(block)
}

fn expand_routes<'cx>(cx: &'cx mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree]) -> Box<MacResult + 'cx> {
	let routes = parse_routes(cx, tts).into_iter().map(|(path, method, handler)| {
		let path_expr = cx.parse_expr(format!("\"{}\"", path));
		let method_expr = cx.expr_path(method);
		mk_tup(sp, vec!(method_expr, path_expr, handler))
	}).collect();

	MacExpr::new(cx.expr_vec(sp, routes))
}

fn parse_routes(cx: &mut ExtCtxt, tts: &[ast::TokenTree]) -> Vec<(String, ast::Path, P<ast::Expr>)> {

	let mut parser = parse::new_parser_from_tts(
		cx.parse_sess(), cx.cfg(), tts.to_vec()
	);

	parse_subroutes("", cx, &mut parser)
}

fn parse_subroutes(base: &str, cx: &mut ExtCtxt, parser: &mut Parser) -> Vec<(String, ast::Path, P<ast::Expr>)> {
	let mut routes = Vec::new();

	while !parser.eat(&token::Eof) {
		match parser.parse_optional_str() {
			Some((ref s, _, _)) => {
				if !parser.eat(&token::FatArrow) {
					parser.expect(&token::FatArrow);
					break;
				}

				let mut new_base = base.to_string();
				match s.container_as_str() {
					Some(s) => {
						new_base.push_str(s.trim_chars('/'));
						new_base.push_str("/");
					},
					None => cx.span_err(parser.span, "invalid path")
				}

				if parser.eat(&token::Eof) {
					cx.span_err(parser.span, "unexpected end of routing tree");
				}

				if parser.eat(&token::OpenDelim(token::Brace)) {
					let subroutes = parse_subroutes(new_base.as_slice(), cx, parser);
					routes.push_all(subroutes.as_slice());

					if parser.eat(&token::CloseDelim(token::Brace)) {
						if !parser.eat(&token::Comma) {
							break;
						}
					} else {
						parser.expect_one_of(&[token::Comma, token::CloseDelim(token::Brace)], &[]);
					}
				} else {
					for (method, handler) in parse_handler(parser).into_iter() {
						routes.push((new_base.clone(), method, handler))
					}

					if !parser.eat(&token::Comma) {
						break;
					}
				}
			},
			None => {
				for (method, handler) in parse_handler(parser).into_iter() {
					routes.push((base.to_string(), method, handler))
				}

				if !parser.eat(&token::Comma) {
					break;
				}
			}
		}
	}

	routes
}

fn parse_handler(parser: &mut Parser) -> Vec<(ast::Path, P<ast::Expr>)> {
	let mut methods = Vec::new();

	loop {
		methods.push(parser.parse_path(parser::NoTypesAllowed).path);

		if parser.eat(&token::Colon) {
			break;
		}

		if !parser.eat(&token::BinOp(token::Or)) {
			parser.expect_one_of(&[token::Colon, token::BinOp(token::Or)], &[]);
		}
	}

	let handler = parser.parse_expr();

	methods.into_iter().map(|m| (m, handler.clone())).collect()
}

fn mk_tup(sp: codemap::Span, content: Vec<P<ast::Expr>>) -> P<ast::Expr> {
	dummy_expr(sp, ast::ExprTup(content))
}

fn dummy_expr(sp: codemap::Span, e: ast::Expr_) -> P<ast::Expr> {
	P(ast::Expr {
		id: ast::DUMMY_NODE_ID,
		node: e,
		span: sp,
	})
}

/**
A macro for assigning content types.

It takes a main type, a sub type and a parameter list. Instead of this:

```
response.headers.content_type = Some(MediaType {
	type_: String::from_str("text"),
	subtype: String::from_str("html"),
	parameters: vec!((String::from_str("charset"), String::from_str("UTF-8")))
});
```

it can be written like this:

```
response.headers.content_type = content_type!("text", "html", "charset": "UTF-8");
```

The `"charset": "UTF-8"` part defines the parameter list for the content type.
It may contain more than one parameter, or be omitted:

```
response.headers.content_type = content_type!("application", "octet-stream", "type": "image/gif", "padding": "4");
```

```
response.headers.content_type = content_type!("image", "png");
```
**/
#[macro_export]
macro_rules! content_type(
	($main_type:expr, $sub_type:expr) => ({
		Some(::http::headers::content_type::MediaType {
			type_: String::from_str($main_type),
			subtype: String::from_str($sub_type),
			parameters: Vec::new()
		})
	});

	($main_type:expr, $sub_type:expr, $($param:expr: $value:expr),+) => ({
		Some(::http::headers::content_type::MediaType {
			type_: String::from_str($main_type),
			subtype: String::from_str($sub_type),
			parameters: vec!( $( (String::from_str($param), String::from_str($value)) ),+ )
		})
	});
)

/**
A macro for callig `send` in response and aborting the handle function if it fails.

This macro will print an error to `stdout` and set the response status to `500 Internal Server Error` if the send fails.
The response status may or may not be written to the client, depending on if the connection is
broken or not, or if the headers have already been written.
**/
#[macro_export]
macro_rules! try_send(
	($response:ident, $content:expr) => (
		match $response.send($content) {
			Ok(v) => v,
			Err(::rustful::ResponseError::IoError(e)) => {
				println!("IO error: {}", e);
				$response.status = http::status::InternalServerError;
				return;
			},
			Err(::rustful::ResponseError::PluginError(e)) => {
				println!("plugin error: {}", e);
				$response.status = http::status::InternalServerError;
				return;
			}
		}
	);

	($response:ident, $content:expr while $what:expr) => (
		match $response.send($content) {
			Ok(v) => v,
			Err(::rustful::ResponseError::IoError(e)) => {
				println!("IO error while {}: {}", $what, e);
				$response.status = http::status::InternalServerError;
				return;
			},
			Err(::rustful::ResponseError::PluginError(e)) => {
				println!("plugin error while {}: {}", $what, e);
				$response.status = http::status::InternalServerError;
				return;
			}
		}
	)
)<commit_msg>parse_path gives a Path<commit_after>#![crate_name = "rustful_macros"]

#![comment = "Macros for rustful"]
#![license = "MIT"]
#![crate_type = "dylib"]

#![doc(html_root_url = "http://ogeon.github.io/rustful/doc/")]

#![feature(macro_rules, plugin_registrar, quote, phase)]

//!This crate provides some helpful macros for rustful, including `router!` and `routes!`.
//!
//!#`router!`
//!The `router!` macro generates a `TreeRouter` containing the provided handlers and routing tree.
//!This can be useful to lower the risk of typing errors, among other things.
//!
//!##Example 1
//!
//!```rust ignore
//!#![feature(phase)]
//!#[phase(plugin)]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!
//!let router = router!{
//!	"/about" => Get: about_us,
//!	"/user/:user" => Get: show_user,
//!	"/product/:name" => Get: show_product,
//!	"/*" => Get: show_error,
//!	"/" => Get: show_welcome
//!};
//!```
//!
//!##Example 2
//!
//!```rust ignore
//!#![feature(phase)]
//!#[phase(plugin)]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!
//!let router = router!{
//!	"/" => Get: show_home,
//!	"home" => Get: show_home,
//!	"user/:username" => {Get: show_user, Post: save_user},
//!	"product" => {
//!		Get: show_all_products,
//!
//!		"json" => Get: send_all_product_data
//!		":id" => {
//!			Get: show_product,
//!			Post | Delete: edit_product,
//!
//!			"json" => Get: send_product_data
//!		}
//!	}
//!};
//!```
//!
//!#`routes!`
//!
//!The `routes!` macro generates a vector of routes, which can be used to create a `TreeRouter`.
//!It has the same syntax as `routes!`.
//!
//!```rust ignore
//!#![feature(phase)]
//!#[phase(plugin)]
//!extern crate rustful_macros;
//!
//!extern crate rustful;
//!
//!...
//!
//!
//!let routes = routes!{
//!	"/" => Get: show_home,
//!	"home" => Get: show_home,
//!	"user/:username" => {Get: show_user, Post: save_user},
//!	"product" => {
//!		Get: show_all_products,
//!
//!		"json" => Get: send_all_product_data
//!		":id" => {
//!			Get: show_product,
//!			Post | Delete: edit_product,
//!
//!			"json" => Get: send_product_data
//!		}
//!	}
//!};
//!
//!let router = TreeRouter::from_routes(&routes);
//!```

extern crate syntax;
extern crate rustc;

use std::path::BytesContainer;

use syntax::{ast, codemap};
use syntax::ext::base::{
	ExtCtxt, MacResult, MacExpr,
	NormalTT, TTMacroExpander
};
use syntax::ext::build::AstBuilder;
use syntax::ext::quote::rt::ExtParseUtils;
use syntax::parse;
use syntax::parse::token;
use syntax::parse::parser;
use syntax::parse::parser::Parser;
use syntax::ptr::P;
//use syntax::print::pprust;

use rustc::plugin::Registry;

#[plugin_registrar]
#[doc(hidden)]
pub fn macro_registrar(reg: &mut Registry) {
	let expander = box expand_router as Box<TTMacroExpander>;
	reg.register_syntax_extension(token::intern("router"), NormalTT(expander, None));

	let expander = box expand_routes as Box<TTMacroExpander>;
	reg.register_syntax_extension(token::intern("routes"), NormalTT(expander, None));
}

fn expand_router<'cx>(cx: &'cx mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree]) -> Box<MacResult + 'cx> {
	let router_ident = cx.ident_of("router");
	let insert_method = cx.ident_of("insert_item");

	let mut calls: Vec<P<ast::Stmt>> = vec!(
		cx.stmt_let(sp, true, router_ident, quote_expr!(&cx, ::rustful::TreeRouter::new()))
	);

	for (path, method, handler) in parse_routes(cx, tts).into_iter() {
		let path_expr = cx.parse_expr(format!("\"{}\"", path));
		let method_expr = cx.expr_path(method);
		calls.push(cx.stmt_expr(
			cx.expr_method_call(sp, cx.expr_ident(sp, router_ident), insert_method, vec!(method_expr, path_expr, handler))
		));
	}

	let block = cx.expr_block(cx.block(sp, calls, Some(cx.expr_ident(sp, router_ident))));

	MacExpr::new(block)
}

fn expand_routes<'cx>(cx: &'cx mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree]) -> Box<MacResult + 'cx> {
	let routes = parse_routes(cx, tts).into_iter().map(|(path, method, handler)| {
		let path_expr = cx.parse_expr(format!("\"{}\"", path));
		let method_expr = cx.expr_path(method);
		mk_tup(sp, vec!(method_expr, path_expr, handler))
	}).collect();

	MacExpr::new(cx.expr_vec(sp, routes))
}

fn parse_routes(cx: &mut ExtCtxt, tts: &[ast::TokenTree]) -> Vec<(String, ast::Path, P<ast::Expr>)> {

	let mut parser = parse::new_parser_from_tts(
		cx.parse_sess(), cx.cfg(), tts.to_vec()
	);

	parse_subroutes("", cx, &mut parser)
}

fn parse_subroutes(base: &str, cx: &mut ExtCtxt, parser: &mut Parser) -> Vec<(String, ast::Path, P<ast::Expr>)> {
	let mut routes = Vec::new();

	while !parser.eat(&token::Eof) {
		match parser.parse_optional_str() {
			Some((ref s, _, _)) => {
				if !parser.eat(&token::FatArrow) {
					parser.expect(&token::FatArrow);
					break;
				}

				let mut new_base = base.to_string();
				match s.container_as_str() {
					Some(s) => {
						new_base.push_str(s.trim_chars('/'));
						new_base.push_str("/");
					},
					None => cx.span_err(parser.span, "invalid path")
				}

				if parser.eat(&token::Eof) {
					cx.span_err(parser.span, "unexpected end of routing tree");
				}

				if parser.eat(&token::OpenDelim(token::Brace)) {
					let subroutes = parse_subroutes(new_base.as_slice(), cx, parser);
					routes.push_all(subroutes.as_slice());

					if parser.eat(&token::CloseDelim(token::Brace)) {
						if !parser.eat(&token::Comma) {
							break;
						}
					} else {
						parser.expect_one_of(&[token::Comma, token::CloseDelim(token::Brace)], &[]);
					}
				} else {
					for (method, handler) in parse_handler(parser).into_iter() {
						routes.push((new_base.clone(), method, handler))
					}

					if !parser.eat(&token::Comma) {
						break;
					}
				}
			},
			None => {
				for (method, handler) in parse_handler(parser).into_iter() {
					routes.push((base.to_string(), method, handler))
				}

				if !parser.eat(&token::Comma) {
					break;
				}
			}
		}
	}

	routes
}

fn parse_handler(parser: &mut Parser) -> Vec<(ast::Path, P<ast::Expr>)> {
	let mut methods = Vec::new();

	loop {
		methods.push(parser.parse_path(parser::NoTypesAllowed));

		if parser.eat(&token::Colon) {
			break;
		}

		if !parser.eat(&token::BinOp(token::Or)) {
			parser.expect_one_of(&[token::Colon, token::BinOp(token::Or)], &[]);
		}
	}

	let handler = parser.parse_expr();

	methods.into_iter().map(|m| (m, handler.clone())).collect()
}

fn mk_tup(sp: codemap::Span, content: Vec<P<ast::Expr>>) -> P<ast::Expr> {
	dummy_expr(sp, ast::ExprTup(content))
}

fn dummy_expr(sp: codemap::Span, e: ast::Expr_) -> P<ast::Expr> {
	P(ast::Expr {
		id: ast::DUMMY_NODE_ID,
		node: e,
		span: sp,
	})
}

/**
A macro for assigning content types.

It takes a main type, a sub type and a parameter list. Instead of this:

```
response.headers.content_type = Some(MediaType {
	type_: String::from_str("text"),
	subtype: String::from_str("html"),
	parameters: vec!((String::from_str("charset"), String::from_str("UTF-8")))
});
```

it can be written like this:

```
response.headers.content_type = content_type!("text", "html", "charset": "UTF-8");
```

The `"charset": "UTF-8"` part defines the parameter list for the content type.
It may contain more than one parameter, or be omitted:

```
response.headers.content_type = content_type!("application", "octet-stream", "type": "image/gif", "padding": "4");
```

```
response.headers.content_type = content_type!("image", "png");
```
**/
#[macro_export]
macro_rules! content_type(
	($main_type:expr, $sub_type:expr) => ({
		Some(::http::headers::content_type::MediaType {
			type_: String::from_str($main_type),
			subtype: String::from_str($sub_type),
			parameters: Vec::new()
		})
	});

	($main_type:expr, $sub_type:expr, $($param:expr: $value:expr),+) => ({
		Some(::http::headers::content_type::MediaType {
			type_: String::from_str($main_type),
			subtype: String::from_str($sub_type),
			parameters: vec!( $( (String::from_str($param), String::from_str($value)) ),+ )
		})
	});
)

/**
A macro for callig `send` in response and aborting the handle function if it fails.

This macro will print an error to `stdout` and set the response status to `500 Internal Server Error` if the send fails.
The response status may or may not be written to the client, depending on if the connection is
broken or not, or if the headers have already been written.
**/
#[macro_export]
macro_rules! try_send(
	($response:ident, $content:expr) => (
		match $response.send($content) {
			Ok(v) => v,
			Err(::rustful::ResponseError::IoError(e)) => {
				println!("IO error: {}", e);
				$response.status = http::status::InternalServerError;
				return;
			},
			Err(::rustful::ResponseError::PluginError(e)) => {
				println!("plugin error: {}", e);
				$response.status = http::status::InternalServerError;
				return;
			}
		}
	);

	($response:ident, $content:expr while $what:expr) => (
		match $response.send($content) {
			Ok(v) => v,
			Err(::rustful::ResponseError::IoError(e)) => {
				println!("IO error while {}: {}", $what, e);
				$response.status = http::status::InternalServerError;
				return;
			},
			Err(::rustful::ResponseError::PluginError(e)) => {
				println!("plugin error while {}: {}", $what, e);
				$response.status = http::status::InternalServerError;
				return;
			}
		}
	)
)<|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 infer::at::At;
use infer::InferOk;
use infer::canonical::OriginalQueryValues;
use std::iter::FromIterator;
use syntax::source_map::Span;
use ty::subst::Kind;
use ty::{self, Ty, TyCtxt};

impl<'cx, 'gcx, 'tcx> At<'cx, 'gcx, 'tcx> {
    /// Given a type `ty` of some value being dropped, computes a set
    /// of "kinds" (types, regions) that must be outlive the execution
    /// of the destructor. These basically correspond to data that the
    /// destructor might access. This is used during regionck to
    /// impose "outlives" constraints on any lifetimes referenced
    /// within.
    ///
    /// The rules here are given by the "dropck" RFCs, notably [#1238]
    /// and [#1327]. This is a fixed-point computation, where we
    /// explore all the data that will be dropped (transitively) when
    /// a value of type `ty` is dropped. For each type T that will be
    /// dropped and which has a destructor, we must assume that all
    /// the types/regions of T are live during the destructor, unless
    /// they are marked with a special attribute (`#[may_dangle]`).
    ///
    /// [#1238]: https://github.com/rust-lang/rfcs/blob/master/text/1238-nonparametric-dropck.md
    /// [#1327]: https://github.com/rust-lang/rfcs/blob/master/text/1327-dropck-param-eyepatch.md
    pub fn dropck_outlives(&self, ty: Ty<'tcx>) -> InferOk<'tcx, Vec<Kind<'tcx>>> {
        debug!(
            "dropck_outlives(ty={:?}, param_env={:?})",
            ty, self.param_env,
        );

        // Quick check: there are a number of cases that we know do not require
        // any destructor.
        let tcx = self.infcx.tcx;
        if trivial_dropck_outlives(tcx, ty) {
            return InferOk {
                value: vec![],
                obligations: vec![],
            };
        }

        let gcx = tcx.global_tcx();
        let mut orig_values = OriginalQueryValues::default();
        let c_ty = self.infcx.canonicalize_query(&self.param_env.and(ty), &mut orig_values);
        let span = self.cause.span;
        debug!("c_ty = {:?}", c_ty);
        match &gcx.dropck_outlives(c_ty) {
            Ok(result) if result.is_proven() => {
                if let Ok(InferOk { value, obligations }) =
                    self.infcx.instantiate_query_response_and_region_obligations(
                    self.cause,
                    self.param_env,
                    &orig_values,
                    result)
                {
                    let ty = self.infcx.resolve_type_vars_if_possible(&ty);
                    let kinds = value.into_kinds_reporting_overflows(tcx, span, ty);
                    return InferOk {
                        value: kinds,
                        obligations,
                    };
                }
            }

            _ => { /* fallthrough to error-handling code below */ }
        }

        // Errors and ambiuity in dropck occur in two cases:
        // - unresolved inference variables at the end of typeck
        // - non well-formed types where projections cannot be resolved
        // Either of these should have created an error before.
        tcx.sess
            .delay_span_bug(span, "dtorck encountered internal error");
        return InferOk {
            value: vec![],
            obligations: vec![],
        };
    }
}

#[derive(Clone, Debug, Default)]
pub struct DropckOutlivesResult<'tcx> {
    pub kinds: Vec<Kind<'tcx>>,
    pub overflows: Vec<Ty<'tcx>>,
}

impl<'tcx> DropckOutlivesResult<'tcx> {
    pub fn report_overflows(
        &self,
        tcx: TyCtxt<'_, '_, 'tcx>,
        span: Span,
        ty: Ty<'tcx>,
    ) {
        for overflow_ty in self.overflows.iter().take(1) {
            let mut err = struct_span_err!(
                tcx.sess,
                span,
                E0320,
                "overflow while adding drop-check rules for {}",
                ty,
            );
            err.note(&format!("overflowed on {}", overflow_ty));
            err.emit();
        }
    }

    pub fn into_kinds_reporting_overflows(
        self,
        tcx: TyCtxt<'_, '_, 'tcx>,
        span: Span,
        ty: Ty<'tcx>,
    ) -> Vec<Kind<'tcx>> {
        self.report_overflows(tcx, span, ty);
        let DropckOutlivesResult { kinds, overflows: _ } = self;
        kinds
    }
}

/// A set of constraints that need to be satisfied in order for
/// a type to be valid for destruction.
#[derive(Clone, Debug)]
pub struct DtorckConstraint<'tcx> {
    /// Types that are required to be alive in order for this
    /// type to be valid for destruction.
    pub outlives: Vec<ty::subst::Kind<'tcx>>,

    /// Types that could not be resolved: projections and params.
    pub dtorck_types: Vec<Ty<'tcx>>,

    /// If, during the computation of the dtorck constraint, we
    /// overflow, that gets recorded here. The caller is expected to
    /// report an error.
    pub overflows: Vec<Ty<'tcx>>,
}

impl<'tcx> DtorckConstraint<'tcx> {
    pub fn empty() -> DtorckConstraint<'tcx> {
        DtorckConstraint {
            outlives: vec![],
            dtorck_types: vec![],
            overflows: vec![],
        }
    }
}

impl<'tcx> FromIterator<DtorckConstraint<'tcx>> for DtorckConstraint<'tcx> {
    fn from_iter<I: IntoIterator<Item = DtorckConstraint<'tcx>>>(iter: I) -> Self {
        let mut result = Self::empty();

        for DtorckConstraint { outlives, dtorck_types, overflows } in iter {
            result.outlives.extend(outlives);
            result.dtorck_types.extend(dtorck_types);
            result.overflows.extend(overflows);
        }

        result
    }
}
BraceStructTypeFoldableImpl! {
    impl<'tcx> TypeFoldable<'tcx> for DropckOutlivesResult<'tcx> {
        kinds, overflows
    }
}

BraceStructLiftImpl! {
    impl<'a, 'tcx> Lift<'tcx> for DropckOutlivesResult<'a> {
        type Lifted = DropckOutlivesResult<'tcx>;
        kinds, overflows
    }
}

impl_stable_hash_for!(struct DropckOutlivesResult<'tcx> {
    kinds, overflows
});

impl_stable_hash_for!(struct DtorckConstraint<'tcx> {
    outlives,
    dtorck_types,
    overflows
});

/// This returns true if the type `ty` is "trivial" for
/// dropck-outlives -- that is, if it doesn't require any types to
/// outlive. This is similar but not *quite* the same as the
/// `needs_drop` test in the compiler already -- that is, for every
/// type T for which this function return true, needs-drop would
/// return false. But the reverse does not hold: in particular,
/// `needs_drop` returns false for `PhantomData`, but it is not
/// trivial for dropck-outlives.
///
/// Note also that `needs_drop` requires a "global" type (i.e., one
/// with erased regions), but this function does not.
pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'_, '_, 'tcx>, ty: Ty<'tcx>) -> bool {
    match ty.sty {
        // None of these types have a destructor and hence they do not
        // require anything in particular to outlive the dtor's
        // execution.
        ty::Infer(ty::FreshIntTy(_))
        | ty::Infer(ty::FreshFloatTy(_))
        | ty::Bool
        | ty::Int(_)
        | ty::Uint(_)
        | ty::Float(_)
        | ty::Never
        | ty::FnDef(..)
        | ty::FnPtr(_)
        | ty::Char
        | ty::GeneratorWitness(..)
        | ty::RawPtr(_)
        | ty::Ref(..)
        | ty::Str
        | ty::Foreign(..)
        | ty::Error => true,

        // [T; N] and [T] have same properties as T.
        ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, ty),

        // (T1..Tn) and closures have same properties as T1..Tn --
        // check if *any* of those are trivial.
        ty::Tuple(ref tys) => tys.iter().cloned().all(|t| trivial_dropck_outlives(tcx, t)),
        ty::Closure(def_id, ref substs) => substs
            .upvar_tys(def_id, tcx)
            .all(|t| trivial_dropck_outlives(tcx, t)),

        ty::Adt(def, _) => {
            if Some(def.did) == tcx.lang_items().manually_drop() {
                // `ManuallyDrop` never has a dtor.
                true
            } else {
                // Other types might. Moreover, PhantomData doesn't
                // have a dtor, but it is considered to own its
                // content, so it is non-trivial. Unions can have `impl Drop`,
                // and hence are non-trivial as well.
                false
            }
        }

        // The following *might* require a destructor: needs deeper inspection.
        ty::Dynamic(..)
        | ty::Projection(..)
        | ty::Param(_)
        | ty::Opaque(..)
        | ty::Placeholder(..)
        | ty::Infer(_)
        | ty::Bound(..)
        | ty::Generator(..) => false,

        ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"),
    }
}
<commit_msg>Rollup merge of #56984 - ljedrz:dropck_outlives_tweaks, r=oli-obk<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 infer::at::At;
use infer::InferOk;
use infer::canonical::OriginalQueryValues;
use std::iter::FromIterator;
use syntax::source_map::Span;
use ty::subst::Kind;
use ty::{self, Ty, TyCtxt};

impl<'cx, 'gcx, 'tcx> At<'cx, 'gcx, 'tcx> {
    /// Given a type `ty` of some value being dropped, computes a set
    /// of "kinds" (types, regions) that must be outlive the execution
    /// of the destructor. These basically correspond to data that the
    /// destructor might access. This is used during regionck to
    /// impose "outlives" constraints on any lifetimes referenced
    /// within.
    ///
    /// The rules here are given by the "dropck" RFCs, notably [#1238]
    /// and [#1327]. This is a fixed-point computation, where we
    /// explore all the data that will be dropped (transitively) when
    /// a value of type `ty` is dropped. For each type T that will be
    /// dropped and which has a destructor, we must assume that all
    /// the types/regions of T are live during the destructor, unless
    /// they are marked with a special attribute (`#[may_dangle]`).
    ///
    /// [#1238]: https://github.com/rust-lang/rfcs/blob/master/text/1238-nonparametric-dropck.md
    /// [#1327]: https://github.com/rust-lang/rfcs/blob/master/text/1327-dropck-param-eyepatch.md
    pub fn dropck_outlives(&self, ty: Ty<'tcx>) -> InferOk<'tcx, Vec<Kind<'tcx>>> {
        debug!(
            "dropck_outlives(ty={:?}, param_env={:?})",
            ty, self.param_env,
        );

        // Quick check: there are a number of cases that we know do not require
        // any destructor.
        let tcx = self.infcx.tcx;
        if trivial_dropck_outlives(tcx, ty) {
            return InferOk {
                value: vec![],
                obligations: vec![],
            };
        }

        let gcx = tcx.global_tcx();
        let mut orig_values = OriginalQueryValues::default();
        let c_ty = self.infcx.canonicalize_query(&self.param_env.and(ty), &mut orig_values);
        let span = self.cause.span;
        debug!("c_ty = {:?}", c_ty);
        if let Ok(result) = &gcx.dropck_outlives(c_ty) {
            if result.is_proven() {
                if let Ok(InferOk { value, obligations }) =
                    self.infcx.instantiate_query_response_and_region_obligations(
                    self.cause,
                    self.param_env,
                    &orig_values,
                    result)
                {
                    let ty = self.infcx.resolve_type_vars_if_possible(&ty);
                    let kinds = value.into_kinds_reporting_overflows(tcx, span, ty);
                    return InferOk {
                        value: kinds,
                        obligations,
                    };
                }
            }
        }

        // Errors and ambiuity in dropck occur in two cases:
        // - unresolved inference variables at the end of typeck
        // - non well-formed types where projections cannot be resolved
        // Either of these should have created an error before.
        tcx.sess
            .delay_span_bug(span, "dtorck encountered internal error");

        InferOk {
            value: vec![],
            obligations: vec![],
        }
    }
}

#[derive(Clone, Debug, Default)]
pub struct DropckOutlivesResult<'tcx> {
    pub kinds: Vec<Kind<'tcx>>,
    pub overflows: Vec<Ty<'tcx>>,
}

impl<'tcx> DropckOutlivesResult<'tcx> {
    pub fn report_overflows(
        &self,
        tcx: TyCtxt<'_, '_, 'tcx>,
        span: Span,
        ty: Ty<'tcx>,
    ) {
        if let Some(overflow_ty) = self.overflows.iter().next() {
            let mut err = struct_span_err!(
                tcx.sess,
                span,
                E0320,
                "overflow while adding drop-check rules for {}",
                ty,
            );
            err.note(&format!("overflowed on {}", overflow_ty));
            err.emit();
        }
    }

    pub fn into_kinds_reporting_overflows(
        self,
        tcx: TyCtxt<'_, '_, 'tcx>,
        span: Span,
        ty: Ty<'tcx>,
    ) -> Vec<Kind<'tcx>> {
        self.report_overflows(tcx, span, ty);
        let DropckOutlivesResult { kinds, overflows: _ } = self;
        kinds
    }
}

/// A set of constraints that need to be satisfied in order for
/// a type to be valid for destruction.
#[derive(Clone, Debug)]
pub struct DtorckConstraint<'tcx> {
    /// Types that are required to be alive in order for this
    /// type to be valid for destruction.
    pub outlives: Vec<ty::subst::Kind<'tcx>>,

    /// Types that could not be resolved: projections and params.
    pub dtorck_types: Vec<Ty<'tcx>>,

    /// If, during the computation of the dtorck constraint, we
    /// overflow, that gets recorded here. The caller is expected to
    /// report an error.
    pub overflows: Vec<Ty<'tcx>>,
}

impl<'tcx> DtorckConstraint<'tcx> {
    pub fn empty() -> DtorckConstraint<'tcx> {
        DtorckConstraint {
            outlives: vec![],
            dtorck_types: vec![],
            overflows: vec![],
        }
    }
}

impl<'tcx> FromIterator<DtorckConstraint<'tcx>> for DtorckConstraint<'tcx> {
    fn from_iter<I: IntoIterator<Item = DtorckConstraint<'tcx>>>(iter: I) -> Self {
        let mut result = Self::empty();

        for DtorckConstraint { outlives, dtorck_types, overflows } in iter {
            result.outlives.extend(outlives);
            result.dtorck_types.extend(dtorck_types);
            result.overflows.extend(overflows);
        }

        result
    }
}
BraceStructTypeFoldableImpl! {
    impl<'tcx> TypeFoldable<'tcx> for DropckOutlivesResult<'tcx> {
        kinds, overflows
    }
}

BraceStructLiftImpl! {
    impl<'a, 'tcx> Lift<'tcx> for DropckOutlivesResult<'a> {
        type Lifted = DropckOutlivesResult<'tcx>;
        kinds, overflows
    }
}

impl_stable_hash_for!(struct DropckOutlivesResult<'tcx> {
    kinds, overflows
});

impl_stable_hash_for!(struct DtorckConstraint<'tcx> {
    outlives,
    dtorck_types,
    overflows
});

/// This returns true if the type `ty` is "trivial" for
/// dropck-outlives -- that is, if it doesn't require any types to
/// outlive. This is similar but not *quite* the same as the
/// `needs_drop` test in the compiler already -- that is, for every
/// type T for which this function return true, needs-drop would
/// return false. But the reverse does not hold: in particular,
/// `needs_drop` returns false for `PhantomData`, but it is not
/// trivial for dropck-outlives.
///
/// Note also that `needs_drop` requires a "global" type (i.e., one
/// with erased regions), but this function does not.
pub fn trivial_dropck_outlives<'tcx>(tcx: TyCtxt<'_, '_, 'tcx>, ty: Ty<'tcx>) -> bool {
    match ty.sty {
        // None of these types have a destructor and hence they do not
        // require anything in particular to outlive the dtor's
        // execution.
        ty::Infer(ty::FreshIntTy(_))
        | ty::Infer(ty::FreshFloatTy(_))
        | ty::Bool
        | ty::Int(_)
        | ty::Uint(_)
        | ty::Float(_)
        | ty::Never
        | ty::FnDef(..)
        | ty::FnPtr(_)
        | ty::Char
        | ty::GeneratorWitness(..)
        | ty::RawPtr(_)
        | ty::Ref(..)
        | ty::Str
        | ty::Foreign(..)
        | ty::Error => true,

        // [T; N] and [T] have same properties as T.
        ty::Array(ty, _) | ty::Slice(ty) => trivial_dropck_outlives(tcx, ty),

        // (T1..Tn) and closures have same properties as T1..Tn --
        // check if *any* of those are trivial.
        ty::Tuple(ref tys) => tys.iter().all(|t| trivial_dropck_outlives(tcx, t)),
        ty::Closure(def_id, ref substs) => substs
            .upvar_tys(def_id, tcx)
            .all(|t| trivial_dropck_outlives(tcx, t)),

        ty::Adt(def, _) => {
            if Some(def.did) == tcx.lang_items().manually_drop() {
                // `ManuallyDrop` never has a dtor.
                true
            } else {
                // Other types might. Moreover, PhantomData doesn't
                // have a dtor, but it is considered to own its
                // content, so it is non-trivial. Unions can have `impl Drop`,
                // and hence are non-trivial as well.
                false
            }
        }

        // The following *might* require a destructor: needs deeper inspection.
        ty::Dynamic(..)
        | ty::Projection(..)
        | ty::Param(_)
        | ty::Opaque(..)
        | ty::Placeholder(..)
        | ty::Infer(_)
        | ty::Bound(..)
        | ty::Generator(..) => false,

        ty::UnnormalizedProjection(..) => bug!("only used with chalk-engine"),
    }
}
<|endoftext|>


<commit_before>//! A pass that promotes borrows of constant rvalues.
//!
//! The rvalues considered constant are trees of temps,
//! each with exactly one initialization, and holding
//! a constant value with no interior mutability.
//! They are placed into a new MIR constant body in
//! `promoted` and the borrow rvalue is replaced with
//! a `Literal::Promoted` using the index into `promoted`
//! of that constant MIR.
//!
//! This pass assumes that every use is dominated by an
//! initialization and can otherwise silence errors, if
//! move analysis runs after promotion on broken MIR.

use rustc::mir::*;
use rustc::mir::visit::{PlaceContext, MutatingUseContext, MutVisitor, Visitor};
use rustc::mir::traversal::ReversePostorder;
use rustc::ty::TyCtxt;
use syntax_pos::Span;

use rustc_data_structures::indexed_vec::{IndexVec, Idx};

use std::{iter, mem, usize};

/// State of a temporary during collection and promotion.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum TempState {
    /// No references to this temp.
    Undefined,
    /// One direct assignment and any number of direct uses.
    /// A borrow of this temp is promotable if the assigned
    /// value is qualified as constant.
    Defined {
        location: Location,
        uses: usize
    },
    /// Any other combination of assignments/uses.
    Unpromotable,
    /// This temp was part of an rvalue which got extracted
    /// during promotion and needs cleanup.
    PromotedOut
}

impl TempState {
    pub fn is_promotable(&self) -> bool {
        debug!("is_promotable: self={:?}", self);
        if let TempState::Defined { uses, .. } = *self {
            uses > 0
        } else {
            false
        }
    }
}

/// A "root candidate" for promotion, which will become the
/// returned value in a promoted MIR, unless it's a subset
/// of a larger candidate.
#[derive(Debug)]
pub enum Candidate {
    /// Borrow of a constant temporary.
    Ref(Location),

    /// Currently applied to function calls where the callee has the unstable
    /// `#[rustc_args_required_const]` attribute as well as the SIMD shuffle
    /// intrinsic. The intrinsic requires the arguments are indeed constant and
    /// the attribute currently provides the semantic requirement that arguments
    /// must be constant.
    Argument { bb: BasicBlock, index: usize },
}

struct TempCollector<'tcx> {
    temps: IndexVec<Local, TempState>,
    span: Span,
    mir: &'tcx Mir<'tcx>,
}

impl<'tcx> Visitor<'tcx> for TempCollector<'tcx> {
    fn visit_local(&mut self,
                   &index: &Local,
                   context: PlaceContext<'tcx>,
                   location: Location) {
        debug!("visit_local: index={:?} context={:?} location={:?}", index, context, location);
        // We're only interested in temporaries
        if self.mir.local_kind(index) != LocalKind::Temp {
            return;
        }

        // Ignore drops, if the temp gets promoted,
        // then it's constant and thus drop is noop.
        // Non-uses are also irrelevent.
        if context.is_drop() || !context.is_use() {
            debug!(
                "visit_local: context.is_drop={:?} context.is_use={:?}",
                context.is_drop(), context.is_use(),
            );
            return;
        }

        let temp = &mut self.temps[index];
        debug!("visit_local: temp={:?}", temp);
        if *temp == TempState::Undefined {
            match context {
                PlaceContext::MutatingUse(MutatingUseContext::Store) |
                PlaceContext::MutatingUse(MutatingUseContext::AsmOutput) |
                PlaceContext::MutatingUse(MutatingUseContext::Call) => {
                    *temp = TempState::Defined {
                        location,
                        uses: 0
                    };
                    return;
                }
                _ => { /* mark as unpromotable below */ }
            }
        } else if let TempState::Defined { ref mut uses, .. } = *temp {
            // We always allow borrows, even mutable ones, as we need
            // to promote mutable borrows of some ZSTs e.g., `&mut []`.
            let allowed_use = context.is_borrow() || context.is_nonmutating_use();
            debug!("visit_local: allowed_use={:?}", allowed_use);
            if allowed_use {
                *uses += 1;
                return;
            }
            /* mark as unpromotable below */
        }
        *temp = TempState::Unpromotable;
    }

    fn visit_source_info(&mut self, source_info: &SourceInfo) {
        self.span = source_info.span;
    }
}

pub fn collect_temps(mir: &Mir<'_>,
                     rpo: &mut ReversePostorder<'_, '_>) -> IndexVec<Local, TempState> {
    let mut collector = TempCollector {
        temps: IndexVec::from_elem(TempState::Undefined, &mir.local_decls),
        span: mir.span,
        mir,
    };
    for (bb, data) in rpo {
        collector.visit_basic_block_data(bb, data);
    }
    collector.temps
}

struct Promoter<'a, 'tcx: 'a> {
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    source: &'a mut Mir<'tcx>,
    promoted: Mir<'tcx>,
    temps: &'a mut IndexVec<Local, TempState>,

    /// If true, all nested temps are also kept in the
    /// source MIR, not moved to the promoted MIR.
    keep_original: bool
}

impl<'a, 'tcx> Promoter<'a, 'tcx> {
    fn new_block(&mut self) -> BasicBlock {
        let span = self.promoted.span;
        self.promoted.basic_blocks_mut().push(BasicBlockData {
            statements: vec![],
            terminator: Some(Terminator {
                source_info: SourceInfo {
                    span,
                    scope: OUTERMOST_SOURCE_SCOPE
                },
                kind: TerminatorKind::Return
            }),
            is_cleanup: false
        })
    }

    fn assign(&mut self, dest: Local, rvalue: Rvalue<'tcx>, span: Span) {
        let last = self.promoted.basic_blocks().last().unwrap();
        let data = &mut self.promoted[last];
        data.statements.push(Statement {
            source_info: SourceInfo {
                span,
                scope: OUTERMOST_SOURCE_SCOPE
            },
            kind: StatementKind::Assign(Place::Base(PlaceBase::Local(dest)), box rvalue)
        });
    }

    /// Copies the initialization of this temp to the
    /// promoted MIR, recursing through temps.
    fn promote_temp(&mut self, temp: Local) -> Local {
        let old_keep_original = self.keep_original;
        let loc = match self.temps[temp] {
            TempState::Defined { location, uses } if uses > 0 => {
                if uses > 1 {
                    self.keep_original = true;
                }
                location
            }
            state =>  {
                span_bug!(self.promoted.span, "{:?} not promotable: {:?}",
                          temp, state);
            }
        };
        if !self.keep_original {
            self.temps[temp] = TempState::PromotedOut;
        }

        let no_stmts = self.source[loc.block].statements.len();
        let new_temp = self.promoted.local_decls.push(
            LocalDecl::new_temp(self.source.local_decls[temp].ty,
                                self.source.local_decls[temp].source_info.span));

        debug!("promote({:?} @ {:?}/{:?}, {:?})",
               temp, loc, no_stmts, self.keep_original);

        // First, take the Rvalue or Call out of the source MIR,
        // or duplicate it, depending on keep_original.
        if loc.statement_index < no_stmts {
            let (rvalue, source_info) = {
                let statement = &mut self.source[loc.block].statements[loc.statement_index];
                let rhs = match statement.kind {
                    StatementKind::Assign(_, ref mut rhs) => rhs,
                    _ => {
                        span_bug!(statement.source_info.span, "{:?} is not an assignment",
                                  statement);
                    }
                };

                (if self.keep_original {
                    rhs.clone()
                } else {
                    let unit = box Rvalue::Aggregate(box AggregateKind::Tuple, vec![]);
                    mem::replace(rhs, unit)
                }, statement.source_info)
            };

            let mut rvalue = *rvalue;
            self.visit_rvalue(&mut rvalue, loc);
            self.assign(new_temp, rvalue, source_info.span);
        } else {
            let terminator = if self.keep_original {
                self.source[loc.block].terminator().clone()
            } else {
                let terminator = self.source[loc.block].terminator_mut();
                let target = match terminator.kind {
                    TerminatorKind::Call { destination: Some((_, target)), .. } => target,
                    ref kind => {
                        span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
                    }
                };
                Terminator {
                    source_info: terminator.source_info,
                    kind: mem::replace(&mut terminator.kind, TerminatorKind::Goto {
                        target,
                    })
                }
            };

            match terminator.kind {
                TerminatorKind::Call { mut func, mut args, from_hir_call, .. } => {
                    self.visit_operand(&mut func, loc);
                    for arg in &mut args {
                        self.visit_operand(arg, loc);
                    }

                    let last = self.promoted.basic_blocks().last().unwrap();
                    let new_target = self.new_block();

                    *self.promoted[last].terminator_mut() = Terminator {
                        kind: TerminatorKind::Call {
                            func,
                            args,
                            cleanup: None,
                            destination: Some(
                                (Place::Base(PlaceBase::Local(new_temp)), new_target)
                            ),
                            from_hir_call,
                        },
                        ..terminator
                    };
                }
                ref kind => {
                    span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
                }
            };
        };

        self.keep_original = old_keep_original;
        new_temp
    }

    fn promote_candidate(mut self, candidate: Candidate) {
        let mut operand = {
            let promoted = &mut self.promoted;
            let promoted_id = Promoted::new(self.source.promoted.len());
            let mut promoted_place = |ty, span| {
                promoted.span = span;
                promoted.local_decls[RETURN_PLACE] = LocalDecl::new_return_place(ty, span);
                Place::Base(
                    PlaceBase::Static(box Static{ kind: StaticKind::Promoted(promoted_id), ty })
                )
            };
            let (blocks, local_decls) = self.source.basic_blocks_and_local_decls_mut();
            match candidate {
                Candidate::Ref(loc) => {
                    let ref mut statement = blocks[loc.block].statements[loc.statement_index];
                    match statement.kind {
                        StatementKind::Assign(_, box Rvalue::Ref(_, _, ref mut place)) => {
                            // Find the underlying local for this (necessarily interior) borrow.
                            let mut place = place;
                            while let Place::Projection(ref mut proj) = *place {
                                assert_ne!(proj.elem, ProjectionElem::Deref);
                                place = &mut proj.base;
                            };

                            let ty = place.ty(local_decls, self.tcx).ty;
                            let span = statement.source_info.span;

                            Operand::Move(mem::replace(place, promoted_place(ty, span)))
                        }
                        _ => bug!()
                    }
                }
                Candidate::Argument { bb, index } => {
                    let terminator = blocks[bb].terminator_mut();
                    match terminator.kind {
                        TerminatorKind::Call { ref mut args, .. } => {
                            let ty = args[index].ty(local_decls, self.tcx);
                            let span = terminator.source_info.span;
                            let operand = Operand::Copy(promoted_place(ty, span));
                            mem::replace(&mut args[index], operand)
                        }
                        // We expected a `TerminatorKind::Call` for which we'd like to promote an
                        // argument. `qualify_consts` saw a `TerminatorKind::Call` here, but
                        // we are seeing a `Goto`. That means that the `promote_temps` method
                        // already promoted this call away entirely. This case occurs when calling
                        // a function requiring a constant argument and as that constant value
                        // providing a value whose computation contains another call to a function
                        // requiring a constant argument.
                        TerminatorKind::Goto { .. } => return,
                        _ => bug!()
                    }
                }
            }
        };

        assert_eq!(self.new_block(), START_BLOCK);
        self.visit_operand(&mut operand, Location {
            block: BasicBlock::new(0),
            statement_index: usize::MAX
        });

        let span = self.promoted.span;
        self.assign(RETURN_PLACE, Rvalue::Use(operand), span);
        self.source.promoted.push(self.promoted);
    }
}

/// Replaces all temporaries with their promoted counterparts.
impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> {
    fn visit_local(&mut self,
                   local: &mut Local,
                   _: PlaceContext<'tcx>,
                   _: Location) {
        if self.source.local_kind(*local) == LocalKind::Temp {
            *local = self.promote_temp(*local);
        }
    }
}

pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
                                    tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                    mut temps: IndexVec<Local, TempState>,
                                    candidates: Vec<Candidate>) {
    // Visit candidates in reverse, in case they're nested.
    debug!("promote_candidates({:?})", candidates);

    for candidate in candidates.into_iter().rev() {
        match candidate {
            Candidate::Ref(Location { block, statement_index }) => {
                match mir[block].statements[statement_index].kind {
                    StatementKind::Assign(Place::Base(PlaceBase::Local(local)), _) => {
                        if temps[local] == TempState::PromotedOut {
                            // Already promoted.
                            continue;
                        }
                    }
                    _ => {}
                }
            }
            Candidate::Argument { .. } => {}
        }

        // Declare return place local so that `Mir::new` doesn't complain.
        let initial_locals = iter::once(
            LocalDecl::new_return_place(tcx.types.never, mir.span)
        ).collect();

        let promoter = Promoter {
            promoted: Mir::new(
                IndexVec::new(),
                // FIXME: maybe try to filter this to avoid blowing up
                // memory usage?
                mir.source_scopes.clone(),
                mir.source_scope_local_data.clone(),
                IndexVec::new(),
                None,
                initial_locals,
                IndexVec::new(),
                0,
                vec![],
                mir.span,
                vec![],
            ),
            tcx,
            source: mir,
            temps: &mut temps,
            keep_original: false
        };
        promoter.promote_candidate(candidate);
    }

    // Eliminate assignments to, and drops of promoted temps.
    let promoted = |index: Local| temps[index] == TempState::PromotedOut;
    for block in mir.basic_blocks_mut() {
        block.statements.retain(|statement| {
            match statement.kind {
                StatementKind::Assign(Place::Base(PlaceBase::Local(index)), _) |
                StatementKind::StorageLive(index) |
                StatementKind::StorageDead(index) => {
                    !promoted(index)
                }
                _ => true
            }
        });
        let terminator = block.terminator_mut();
        match terminator.kind {
            TerminatorKind::Drop { location: Place::Base(PlaceBase::Local(index)), target, .. } => {
                if promoted(index) {
                    terminator.kind = TerminatorKind::Goto {
                        target,
                    };
                }
            }
            _ => {}
        }
    }
}
<commit_msg>`asm!` output can neither be promotable nor const<commit_after>//! A pass that promotes borrows of constant rvalues.
//!
//! The rvalues considered constant are trees of temps,
//! each with exactly one initialization, and holding
//! a constant value with no interior mutability.
//! They are placed into a new MIR constant body in
//! `promoted` and the borrow rvalue is replaced with
//! a `Literal::Promoted` using the index into `promoted`
//! of that constant MIR.
//!
//! This pass assumes that every use is dominated by an
//! initialization and can otherwise silence errors, if
//! move analysis runs after promotion on broken MIR.

use rustc::mir::*;
use rustc::mir::visit::{PlaceContext, MutatingUseContext, MutVisitor, Visitor};
use rustc::mir::traversal::ReversePostorder;
use rustc::ty::TyCtxt;
use syntax_pos::Span;

use rustc_data_structures::indexed_vec::{IndexVec, Idx};

use std::{iter, mem, usize};

/// State of a temporary during collection and promotion.
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum TempState {
    /// No references to this temp.
    Undefined,
    /// One direct assignment and any number of direct uses.
    /// A borrow of this temp is promotable if the assigned
    /// value is qualified as constant.
    Defined {
        location: Location,
        uses: usize
    },
    /// Any other combination of assignments/uses.
    Unpromotable,
    /// This temp was part of an rvalue which got extracted
    /// during promotion and needs cleanup.
    PromotedOut
}

impl TempState {
    pub fn is_promotable(&self) -> bool {
        debug!("is_promotable: self={:?}", self);
        if let TempState::Defined { uses, .. } = *self {
            uses > 0
        } else {
            false
        }
    }
}

/// A "root candidate" for promotion, which will become the
/// returned value in a promoted MIR, unless it's a subset
/// of a larger candidate.
#[derive(Debug)]
pub enum Candidate {
    /// Borrow of a constant temporary.
    Ref(Location),

    /// Currently applied to function calls where the callee has the unstable
    /// `#[rustc_args_required_const]` attribute as well as the SIMD shuffle
    /// intrinsic. The intrinsic requires the arguments are indeed constant and
    /// the attribute currently provides the semantic requirement that arguments
    /// must be constant.
    Argument { bb: BasicBlock, index: usize },
}

struct TempCollector<'tcx> {
    temps: IndexVec<Local, TempState>,
    span: Span,
    mir: &'tcx Mir<'tcx>,
}

impl<'tcx> Visitor<'tcx> for TempCollector<'tcx> {
    fn visit_local(&mut self,
                   &index: &Local,
                   context: PlaceContext<'tcx>,
                   location: Location) {
        debug!("visit_local: index={:?} context={:?} location={:?}", index, context, location);
        // We're only interested in temporaries
        if self.mir.local_kind(index) != LocalKind::Temp {
            return;
        }

        // Ignore drops, if the temp gets promoted,
        // then it's constant and thus drop is noop.
        // Non-uses are also irrelevent.
        if context.is_drop() || !context.is_use() {
            debug!(
                "visit_local: context.is_drop={:?} context.is_use={:?}",
                context.is_drop(), context.is_use(),
            );
            return;
        }

        let temp = &mut self.temps[index];
        debug!("visit_local: temp={:?}", temp);
        if *temp == TempState::Undefined {
            match context {
                PlaceContext::MutatingUse(MutatingUseContext::Store) |
                PlaceContext::MutatingUse(MutatingUseContext::Call) => {
                    *temp = TempState::Defined {
                        location,
                        uses: 0
                    };
                    return;
                }
                _ => { /* mark as unpromotable below */ }
            }
        } else if let TempState::Defined { ref mut uses, .. } = *temp {
            // We always allow borrows, even mutable ones, as we need
            // to promote mutable borrows of some ZSTs e.g., `&mut []`.
            let allowed_use = context.is_borrow() || context.is_nonmutating_use();
            debug!("visit_local: allowed_use={:?}", allowed_use);
            if allowed_use {
                *uses += 1;
                return;
            }
            /* mark as unpromotable below */
        }
        *temp = TempState::Unpromotable;
    }

    fn visit_source_info(&mut self, source_info: &SourceInfo) {
        self.span = source_info.span;
    }
}

pub fn collect_temps(mir: &Mir<'_>,
                     rpo: &mut ReversePostorder<'_, '_>) -> IndexVec<Local, TempState> {
    let mut collector = TempCollector {
        temps: IndexVec::from_elem(TempState::Undefined, &mir.local_decls),
        span: mir.span,
        mir,
    };
    for (bb, data) in rpo {
        collector.visit_basic_block_data(bb, data);
    }
    collector.temps
}

struct Promoter<'a, 'tcx: 'a> {
    tcx: TyCtxt<'a, 'tcx, 'tcx>,
    source: &'a mut Mir<'tcx>,
    promoted: Mir<'tcx>,
    temps: &'a mut IndexVec<Local, TempState>,

    /// If true, all nested temps are also kept in the
    /// source MIR, not moved to the promoted MIR.
    keep_original: bool
}

impl<'a, 'tcx> Promoter<'a, 'tcx> {
    fn new_block(&mut self) -> BasicBlock {
        let span = self.promoted.span;
        self.promoted.basic_blocks_mut().push(BasicBlockData {
            statements: vec![],
            terminator: Some(Terminator {
                source_info: SourceInfo {
                    span,
                    scope: OUTERMOST_SOURCE_SCOPE
                },
                kind: TerminatorKind::Return
            }),
            is_cleanup: false
        })
    }

    fn assign(&mut self, dest: Local, rvalue: Rvalue<'tcx>, span: Span) {
        let last = self.promoted.basic_blocks().last().unwrap();
        let data = &mut self.promoted[last];
        data.statements.push(Statement {
            source_info: SourceInfo {
                span,
                scope: OUTERMOST_SOURCE_SCOPE
            },
            kind: StatementKind::Assign(Place::Base(PlaceBase::Local(dest)), box rvalue)
        });
    }

    /// Copies the initialization of this temp to the
    /// promoted MIR, recursing through temps.
    fn promote_temp(&mut self, temp: Local) -> Local {
        let old_keep_original = self.keep_original;
        let loc = match self.temps[temp] {
            TempState::Defined { location, uses } if uses > 0 => {
                if uses > 1 {
                    self.keep_original = true;
                }
                location
            }
            state =>  {
                span_bug!(self.promoted.span, "{:?} not promotable: {:?}",
                          temp, state);
            }
        };
        if !self.keep_original {
            self.temps[temp] = TempState::PromotedOut;
        }

        let no_stmts = self.source[loc.block].statements.len();
        let new_temp = self.promoted.local_decls.push(
            LocalDecl::new_temp(self.source.local_decls[temp].ty,
                                self.source.local_decls[temp].source_info.span));

        debug!("promote({:?} @ {:?}/{:?}, {:?})",
               temp, loc, no_stmts, self.keep_original);

        // First, take the Rvalue or Call out of the source MIR,
        // or duplicate it, depending on keep_original.
        if loc.statement_index < no_stmts {
            let (rvalue, source_info) = {
                let statement = &mut self.source[loc.block].statements[loc.statement_index];
                let rhs = match statement.kind {
                    StatementKind::Assign(_, ref mut rhs) => rhs,
                    _ => {
                        span_bug!(statement.source_info.span, "{:?} is not an assignment",
                                  statement);
                    }
                };

                (if self.keep_original {
                    rhs.clone()
                } else {
                    let unit = box Rvalue::Aggregate(box AggregateKind::Tuple, vec![]);
                    mem::replace(rhs, unit)
                }, statement.source_info)
            };

            let mut rvalue = *rvalue;
            self.visit_rvalue(&mut rvalue, loc);
            self.assign(new_temp, rvalue, source_info.span);
        } else {
            let terminator = if self.keep_original {
                self.source[loc.block].terminator().clone()
            } else {
                let terminator = self.source[loc.block].terminator_mut();
                let target = match terminator.kind {
                    TerminatorKind::Call { destination: Some((_, target)), .. } => target,
                    ref kind => {
                        span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
                    }
                };
                Terminator {
                    source_info: terminator.source_info,
                    kind: mem::replace(&mut terminator.kind, TerminatorKind::Goto {
                        target,
                    })
                }
            };

            match terminator.kind {
                TerminatorKind::Call { mut func, mut args, from_hir_call, .. } => {
                    self.visit_operand(&mut func, loc);
                    for arg in &mut args {
                        self.visit_operand(arg, loc);
                    }

                    let last = self.promoted.basic_blocks().last().unwrap();
                    let new_target = self.new_block();

                    *self.promoted[last].terminator_mut() = Terminator {
                        kind: TerminatorKind::Call {
                            func,
                            args,
                            cleanup: None,
                            destination: Some(
                                (Place::Base(PlaceBase::Local(new_temp)), new_target)
                            ),
                            from_hir_call,
                        },
                        ..terminator
                    };
                }
                ref kind => {
                    span_bug!(terminator.source_info.span, "{:?} not promotable", kind);
                }
            };
        };

        self.keep_original = old_keep_original;
        new_temp
    }

    fn promote_candidate(mut self, candidate: Candidate) {
        let mut operand = {
            let promoted = &mut self.promoted;
            let promoted_id = Promoted::new(self.source.promoted.len());
            let mut promoted_place = |ty, span| {
                promoted.span = span;
                promoted.local_decls[RETURN_PLACE] = LocalDecl::new_return_place(ty, span);
                Place::Base(
                    PlaceBase::Static(box Static{ kind: StaticKind::Promoted(promoted_id), ty })
                )
            };
            let (blocks, local_decls) = self.source.basic_blocks_and_local_decls_mut();
            match candidate {
                Candidate::Ref(loc) => {
                    let ref mut statement = blocks[loc.block].statements[loc.statement_index];
                    match statement.kind {
                        StatementKind::Assign(_, box Rvalue::Ref(_, _, ref mut place)) => {
                            // Find the underlying local for this (necessarily interior) borrow.
                            let mut place = place;
                            while let Place::Projection(ref mut proj) = *place {
                                assert_ne!(proj.elem, ProjectionElem::Deref);
                                place = &mut proj.base;
                            };

                            let ty = place.ty(local_decls, self.tcx).ty;
                            let span = statement.source_info.span;

                            Operand::Move(mem::replace(place, promoted_place(ty, span)))
                        }
                        _ => bug!()
                    }
                }
                Candidate::Argument { bb, index } => {
                    let terminator = blocks[bb].terminator_mut();
                    match terminator.kind {
                        TerminatorKind::Call { ref mut args, .. } => {
                            let ty = args[index].ty(local_decls, self.tcx);
                            let span = terminator.source_info.span;
                            let operand = Operand::Copy(promoted_place(ty, span));
                            mem::replace(&mut args[index], operand)
                        }
                        // We expected a `TerminatorKind::Call` for which we'd like to promote an
                        // argument. `qualify_consts` saw a `TerminatorKind::Call` here, but
                        // we are seeing a `Goto`. That means that the `promote_temps` method
                        // already promoted this call away entirely. This case occurs when calling
                        // a function requiring a constant argument and as that constant value
                        // providing a value whose computation contains another call to a function
                        // requiring a constant argument.
                        TerminatorKind::Goto { .. } => return,
                        _ => bug!()
                    }
                }
            }
        };

        assert_eq!(self.new_block(), START_BLOCK);
        self.visit_operand(&mut operand, Location {
            block: BasicBlock::new(0),
            statement_index: usize::MAX
        });

        let span = self.promoted.span;
        self.assign(RETURN_PLACE, Rvalue::Use(operand), span);
        self.source.promoted.push(self.promoted);
    }
}

/// Replaces all temporaries with their promoted counterparts.
impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> {
    fn visit_local(&mut self,
                   local: &mut Local,
                   _: PlaceContext<'tcx>,
                   _: Location) {
        if self.source.local_kind(*local) == LocalKind::Temp {
            *local = self.promote_temp(*local);
        }
    }
}

pub fn promote_candidates<'a, 'tcx>(mir: &mut Mir<'tcx>,
                                    tcx: TyCtxt<'a, 'tcx, 'tcx>,
                                    mut temps: IndexVec<Local, TempState>,
                                    candidates: Vec<Candidate>) {
    // Visit candidates in reverse, in case they're nested.
    debug!("promote_candidates({:?})", candidates);

    for candidate in candidates.into_iter().rev() {
        match candidate {
            Candidate::Ref(Location { block, statement_index }) => {
                match mir[block].statements[statement_index].kind {
                    StatementKind::Assign(Place::Base(PlaceBase::Local(local)), _) => {
                        if temps[local] == TempState::PromotedOut {
                            // Already promoted.
                            continue;
                        }
                    }
                    _ => {}
                }
            }
            Candidate::Argument { .. } => {}
        }

        // Declare return place local so that `Mir::new` doesn't complain.
        let initial_locals = iter::once(
            LocalDecl::new_return_place(tcx.types.never, mir.span)
        ).collect();

        let promoter = Promoter {
            promoted: Mir::new(
                IndexVec::new(),
                // FIXME: maybe try to filter this to avoid blowing up
                // memory usage?
                mir.source_scopes.clone(),
                mir.source_scope_local_data.clone(),
                IndexVec::new(),
                None,
                initial_locals,
                IndexVec::new(),
                0,
                vec![],
                mir.span,
                vec![],
            ),
            tcx,
            source: mir,
            temps: &mut temps,
            keep_original: false
        };
        promoter.promote_candidate(candidate);
    }

    // Eliminate assignments to, and drops of promoted temps.
    let promoted = |index: Local| temps[index] == TempState::PromotedOut;
    for block in mir.basic_blocks_mut() {
        block.statements.retain(|statement| {
            match statement.kind {
                StatementKind::Assign(Place::Base(PlaceBase::Local(index)), _) |
                StatementKind::StorageLive(index) |
                StatementKind::StorageDead(index) => {
                    !promoted(index)
                }
                _ => true
            }
        });
        let terminator = block.terminator_mut();
        match terminator.kind {
            TerminatorKind::Drop { location: Place::Base(PlaceBase::Local(index)), target, .. } => {
                if promoted(index) {
                    terminator.kind = TerminatorKind::Goto {
                        target,
                    };
                }
            }
            _ => {}
        }
    }
}
<|endoftext|>