<commit_before>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::config::Error;
use diem_secure_storage::{
    GitHubStorage, InMemoryStorage, Namespaced, OnDiskStorage, Storage, VaultStorage,
};
use serde::{Deserialize, Serialize};
use std::{
    fs::File,
    io::Read,
    path::{Path, PathBuf},
};

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(rename_all = "snake_case", tag = "type")]
pub enum SecureBackend {
    GitHub(GitHubConfig),
    InMemoryStorage,
    Vault(VaultConfig),
    OnDiskStorage(OnDiskStorageConfig),
}

impl SecureBackend {
    pub fn namespace(&self) -> Option<&str> {
        match self {
            SecureBackend::GitHub(GitHubConfig { namespace, .. })
            | SecureBackend::Vault(VaultConfig { namespace, .. })
            | SecureBackend::OnDiskStorage(OnDiskStorageConfig { namespace, .. }) => {
                namespace.as_deref()
            }
            SecureBackend::InMemoryStorage => None,
        }
    }
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct GitHubConfig {
    /// The owner or account that hosts a repository
    pub repository_owner: String,
    /// The repository where storage will mount
    pub repository: String,
    /// The branch containing storage, defaults to master
    pub branch: Option<String>,
    /// The authorization token for accessing the repository
    pub token: Token,
    /// A namespace is an optional portion of the path to a key stored within GitHubConfig. For
    /// example, a key, S, without a namespace would be available in S, with a namespace, N, it
    /// would be in N/S.
    pub namespace: Option<String>,
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct VaultConfig {
    /// Optional SSL Certificate for the vault host, this is expected to be a full path.
    pub ca_certificate: Option<PathBuf>,
    /// A namespace is an optional portion of the path to a key stored within Vault. For example,
    /// a secret, S, without a namespace would be available in secret/data/S, with a namespace, N, it
    /// would be in secret/data/N/S.
    pub namespace: Option<String>,
    /// Vault leverages leases on many tokens, specify this to automatically have your lease
    /// renewed up to that many seconds more. If this is not specified, the lease will not
    /// automatically be renewed.
    pub renew_ttl_secs: Option<u32>,
    /// Vault's URL, note: only HTTP is currently supported.
    pub server: String,
    /// The authorization token for accessing secrets
    pub token: Token,
    /// Disable check-and-set when writing secrets to Vault
    pub disable_cas: Option<bool>,
    /// Timeout for new vault socket connections, in milliseconds.
    pub connection_timeout_ms: Option<u64>,
    /// Timeout for generic vault operations (e.g., reads and writes), in milliseconds.
    pub response_timeout_ms: Option<u64>,
}

impl VaultConfig {
    pub fn ca_certificate(&self) -> Result<String, Error> {
        let path = self
            .ca_certificate
            .as_ref()
            .ok_or(Error::Missing("ca_certificate"))?;
        read_file(path)
    }
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct OnDiskStorageConfig {
    // Required path for on disk storage
    pub path: PathBuf,
    /// A namespace is an optional portion of the path to a key stored within OnDiskStorage. For
    /// example, a key, S, without a namespace would be available in S, with a namespace, N, it
    /// would be in N/S.
    pub namespace: Option<String>,
    #[serde(skip)]
    data_dir: PathBuf,
}

/// Tokens can either be directly within this config or stored somewhere on disk.
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum Token {
    FromConfig(String),
    /// This is an absolute path and not relative to data_dir
    FromDisk(PathBuf),
}

impl Token {
    pub fn read_token(&self) -> Result<String, Error> {
        match self {
            Token::FromDisk(path) => read_file(path),
            Token::FromConfig(token) => Ok(token.clone()),
        }
    }
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct TokenFromConfig {
    token: String,
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct TokenFromDisk {
    path: PathBuf,
}

impl Default for OnDiskStorageConfig {
    fn default() -> Self {
        Self {
            namespace: None,
            path: PathBuf::from("secure_storage.json"),
            data_dir: PathBuf::from("/opt/diem/data"),
        }
    }
}

impl OnDiskStorageConfig {
    pub fn path(&self) -> PathBuf {
        if self.path.is_relative() {
            self.data_dir.join(&self.path)
        } else {
            self.path.clone()
        }
    }

    pub fn set_data_dir(&mut self, data_dir: PathBuf) {
        self.data_dir = data_dir;
    }
}

fn read_file(path: &Path) -> Result<String, Error> {
    let mut file =
        File::open(path).map_err(|e| Error::IO(path.to_str().unwrap().to_string(), e))?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)
        .map_err(|e| Error::IO(path.to_str().unwrap().to_string(), e))?;
    Ok(contents)
}

impl From<&SecureBackend> for Storage {
    fn from(backend: &SecureBackend) -> Self {
        match backend {
            SecureBackend::GitHub(config) => {
                let storage = Storage::from(GitHubStorage::new(
                    config.repository_owner.clone(),
                    config.repository.clone(),
                    config
                        .branch
                        .as_ref()
                        .cloned()
                        .unwrap_or_else(|| "master".to_string()),
                    config.token.read_token().expect("Unable to read token"),
                ));
                if let Some(namespace) = &config.namespace {
                    Storage::from(Namespaced::new(namespace, Box::new(storage)))
                } else {
                    storage
                }
            }
            SecureBackend::InMemoryStorage => Storage::from(InMemoryStorage::new()),
            SecureBackend::OnDiskStorage(config) => {
                let storage = Storage::from(OnDiskStorage::new(config.path()));
                if let Some(namespace) = &config.namespace {
                    Storage::from(Namespaced::new(namespace, Box::new(storage)))
                } else {
                    storage
                }
            }
            SecureBackend::Vault(config) => {
                let storage = Storage::from(VaultStorage::new(
                    config.server.clone(),
                    config.token.read_token().expect("Unable to read token"),
                    config
                        .ca_certificate
                        .as_ref()
                        .map(|_| config.ca_certificate().unwrap()),
                    config.renew_ttl_secs,
                    config.disable_cas.map_or_else(|| true, |disable| !disable),
                    config.connection_timeout_ms,
                    config.response_timeout_ms,
                ));
                if let Some(namespace) = &config.namespace {
                    Storage::from(Namespaced::new(namespace, Box::new(storage)))
                } else {
                    storage
                }
            }
        }
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    use std::io::Write;

    #[derive(Debug, Deserialize, PartialEq, Serialize)]
    struct Config {
        vault: VaultConfig,
    }

    #[test]
    fn test_token_config_parsing() {
        let from_config = Config {
            vault: VaultConfig {
                namespace: None,
                server: "127.0.0.1:8200".to_string(),
                ca_certificate: None,
                token: Token::FromConfig("test".to_string()),
                renew_ttl_secs: None,
                disable_cas: None,
                connection_timeout_ms: None,
                response_timeout_ms: None,
            },
        };

        let text_from_config = r#"
vault:
    server: "127.0.0.1:8200"
    token:
        from_config: "test"
        "#;

        let de_from_config: Config = serde_yaml::from_str(text_from_config).unwrap();
        assert_eq!(de_from_config, from_config);
        // Just assert that it can be serialized, not about to do string comparison
        serde_yaml::to_string(&from_config).unwrap();
    }

    #[test]
    fn test_vault_timeout_parsing() {
        let from_config = Config {
            vault: VaultConfig {
                namespace: None,
                server: "127.0.0.1:8200".to_string(),
                ca_certificate: None,
                token: Token::FromConfig("test".to_string()),
                renew_ttl_secs: None,
                disable_cas: None,
                connection_timeout_ms: Some(3000),
                response_timeout_ms: Some(5000),
            },
        };

        let text_from_config = r#"
vault:
    server: "127.0.0.1:8200"
    token:
        from_config: "test"
    connection_timeout_ms: 3000
    response_timeout_ms: 5000
        "#;

        let de_from_config: Config = serde_yaml::from_str(text_from_config).unwrap();
        assert_eq!(de_from_config, from_config);
        // Just assert that it can be serialized, no need to do string comparison
        serde_yaml::to_string(&from_config).unwrap();
    }

    #[test]
    fn test_token_disk_parsing() {
        let from_disk = Config {
            vault: VaultConfig {
                namespace: None,
                server: "127.0.0.1:8200".to_string(),
                ca_certificate: None,
                token: Token::FromDisk(PathBuf::from("/token")),
                renew_ttl_secs: None,
                disable_cas: None,
                connection_timeout_ms: None,
                response_timeout_ms: None,
            },
        };

        let text_from_disk = r#"
vault:
    server: "127.0.0.1:8200"
    token:
        from_disk: "/token"
        "#;

        let de_from_disk: Config = serde_yaml::from_str(text_from_disk).unwrap();
        assert_eq!(de_from_disk, from_disk);
        // Just assert that it can be serialized, not about to do string comparison
        serde_yaml::to_string(&from_disk).unwrap();
    }

    #[test]
    fn test_token_reading() {
        let temppath = diem_temppath::TempPath::new();
        temppath.create_as_file().unwrap();
        let mut file = File::create(temppath.path()).unwrap();
        file.write_all(b"disk_token").unwrap();

        let disk = Token::FromDisk(temppath.path().to_path_buf());
        assert_eq!("disk_token", disk.read_token().unwrap());

        let config = Token::FromConfig("config_token".to_string());
        assert_eq!("config_token", config.read_token().unwrap());
    }
}
<commit_msg>config: add helper for clearing a backend namespace<commit_after>// Copyright (c) The Diem Core Contributors
// SPDX-License-Identifier: Apache-2.0

use crate::config::Error;
use diem_secure_storage::{
    GitHubStorage, InMemoryStorage, Namespaced, OnDiskStorage, Storage, VaultStorage,
};
use serde::{Deserialize, Serialize};
use std::{
    fs::File,
    io::Read,
    path::{Path, PathBuf},
};

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(rename_all = "snake_case", tag = "type")]
pub enum SecureBackend {
    GitHub(GitHubConfig),
    InMemoryStorage,
    Vault(VaultConfig),
    OnDiskStorage(OnDiskStorageConfig),
}

impl SecureBackend {
    pub fn namespace(&self) -> Option<&str> {
        match self {
            SecureBackend::GitHub(GitHubConfig { namespace, .. })
            | SecureBackend::Vault(VaultConfig { namespace, .. })
            | SecureBackend::OnDiskStorage(OnDiskStorageConfig { namespace, .. }) => {
                namespace.as_deref()
            }
            SecureBackend::InMemoryStorage => None,
        }
    }

    pub fn clear_namespace(&mut self) {
        match self {
            SecureBackend::GitHub(GitHubConfig { namespace, .. })
            | SecureBackend::Vault(VaultConfig { namespace, .. })
            | SecureBackend::OnDiskStorage(OnDiskStorageConfig { namespace, .. }) => {
                *namespace = None;
            }
            SecureBackend::InMemoryStorage => {}
        }
    }
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct GitHubConfig {
    /// The owner or account that hosts a repository
    pub repository_owner: String,
    /// The repository where storage will mount
    pub repository: String,
    /// The branch containing storage, defaults to master
    pub branch: Option<String>,
    /// The authorization token for accessing the repository
    pub token: Token,
    /// A namespace is an optional portion of the path to a key stored within GitHubConfig. For
    /// example, a key, S, without a namespace would be available in S, with a namespace, N, it
    /// would be in N/S.
    pub namespace: Option<String>,
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct VaultConfig {
    /// Optional SSL Certificate for the vault host, this is expected to be a full path.
    pub ca_certificate: Option<PathBuf>,
    /// A namespace is an optional portion of the path to a key stored within Vault. For example,
    /// a secret, S, without a namespace would be available in secret/data/S, with a namespace, N, it
    /// would be in secret/data/N/S.
    pub namespace: Option<String>,
    /// Vault leverages leases on many tokens, specify this to automatically have your lease
    /// renewed up to that many seconds more. If this is not specified, the lease will not
    /// automatically be renewed.
    pub renew_ttl_secs: Option<u32>,
    /// Vault's URL, note: only HTTP is currently supported.
    pub server: String,
    /// The authorization token for accessing secrets
    pub token: Token,
    /// Disable check-and-set when writing secrets to Vault
    pub disable_cas: Option<bool>,
    /// Timeout for new vault socket connections, in milliseconds.
    pub connection_timeout_ms: Option<u64>,
    /// Timeout for generic vault operations (e.g., reads and writes), in milliseconds.
    pub response_timeout_ms: Option<u64>,
}

impl VaultConfig {
    pub fn ca_certificate(&self) -> Result<String, Error> {
        let path = self
            .ca_certificate
            .as_ref()
            .ok_or(Error::Missing("ca_certificate"))?;
        read_file(path)
    }
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct OnDiskStorageConfig {
    // Required path for on disk storage
    pub path: PathBuf,
    /// A namespace is an optional portion of the path to a key stored within OnDiskStorage. For
    /// example, a key, S, without a namespace would be available in S, with a namespace, N, it
    /// would be in N/S.
    pub namespace: Option<String>,
    #[serde(skip)]
    data_dir: PathBuf,
}

/// Tokens can either be directly within this config or stored somewhere on disk.
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum Token {
    FromConfig(String),
    /// This is an absolute path and not relative to data_dir
    FromDisk(PathBuf),
}

impl Token {
    pub fn read_token(&self) -> Result<String, Error> {
        match self {
            Token::FromDisk(path) => read_file(path),
            Token::FromConfig(token) => Ok(token.clone()),
        }
    }
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct TokenFromConfig {
    token: String,
}

#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct TokenFromDisk {
    path: PathBuf,
}

impl Default for OnDiskStorageConfig {
    fn default() -> Self {
        Self {
            namespace: None,
            path: PathBuf::from("secure_storage.json"),
            data_dir: PathBuf::from("/opt/diem/data"),
        }
    }
}

impl OnDiskStorageConfig {
    pub fn path(&self) -> PathBuf {
        if self.path.is_relative() {
            self.data_dir.join(&self.path)
        } else {
            self.path.clone()
        }
    }

    pub fn set_data_dir(&mut self, data_dir: PathBuf) {
        self.data_dir = data_dir;
    }
}

fn read_file(path: &Path) -> Result<String, Error> {
    let mut file =
        File::open(path).map_err(|e| Error::IO(path.to_str().unwrap().to_string(), e))?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)
        .map_err(|e| Error::IO(path.to_str().unwrap().to_string(), e))?;
    Ok(contents)
}

impl From<&SecureBackend> for Storage {
    fn from(backend: &SecureBackend) -> Self {
        match backend {
            SecureBackend::GitHub(config) => {
                let storage = Storage::from(GitHubStorage::new(
                    config.repository_owner.clone(),
                    config.repository.clone(),
                    config
                        .branch
                        .as_ref()
                        .cloned()
                        .unwrap_or_else(|| "master".to_string()),
                    config.token.read_token().expect("Unable to read token"),
                ));
                if let Some(namespace) = &config.namespace {
                    Storage::from(Namespaced::new(namespace, Box::new(storage)))
                } else {
                    storage
                }
            }
            SecureBackend::InMemoryStorage => Storage::from(InMemoryStorage::new()),
            SecureBackend::OnDiskStorage(config) => {
                let storage = Storage::from(OnDiskStorage::new(config.path()));
                if let Some(namespace) = &config.namespace {
                    Storage::from(Namespaced::new(namespace, Box::new(storage)))
                } else {
                    storage
                }
            }
            SecureBackend::Vault(config) => {
                let storage = Storage::from(VaultStorage::new(
                    config.server.clone(),
                    config.token.read_token().expect("Unable to read token"),
                    config
                        .ca_certificate
                        .as_ref()
                        .map(|_| config.ca_certificate().unwrap()),
                    config.renew_ttl_secs,
                    config.disable_cas.map_or_else(|| true, |disable| !disable),
                    config.connection_timeout_ms,
                    config.response_timeout_ms,
                ));
                if let Some(namespace) = &config.namespace {
                    Storage::from(Namespaced::new(namespace, Box::new(storage)))
                } else {
                    storage
                }
            }
        }
    }
}
#[cfg(test)]
mod tests {
    use super::*;
    use std::io::Write;

    #[derive(Debug, Deserialize, PartialEq, Serialize)]
    struct Config {
        vault: VaultConfig,
    }

    #[test]
    fn test_token_config_parsing() {
        let from_config = Config {
            vault: VaultConfig {
                namespace: None,
                server: "127.0.0.1:8200".to_string(),
                ca_certificate: None,
                token: Token::FromConfig("test".to_string()),
                renew_ttl_secs: None,
                disable_cas: None,
                connection_timeout_ms: None,
                response_timeout_ms: None,
            },
        };

        let text_from_config = r#"
vault:
    server: "127.0.0.1:8200"
    token:
        from_config: "test"
        "#;

        let de_from_config: Config = serde_yaml::from_str(text_from_config).unwrap();
        assert_eq!(de_from_config, from_config);
        // Just assert that it can be serialized, not about to do string comparison
        serde_yaml::to_string(&from_config).unwrap();
    }

    #[test]
    fn test_vault_timeout_parsing() {
        let from_config = Config {
            vault: VaultConfig {
                namespace: None,
                server: "127.0.0.1:8200".to_string(),
                ca_certificate: None,
                token: Token::FromConfig("test".to_string()),
                renew_ttl_secs: None,
                disable_cas: None,
                connection_timeout_ms: Some(3000),
                response_timeout_ms: Some(5000),
            },
        };

        let text_from_config = r#"
vault:
    server: "127.0.0.1:8200"
    token:
        from_config: "test"
    connection_timeout_ms: 3000
    response_timeout_ms: 5000
        "#;

        let de_from_config: Config = serde_yaml::from_str(text_from_config).unwrap();
        assert_eq!(de_from_config, from_config);
        // Just assert that it can be serialized, no need to do string comparison
        serde_yaml::to_string(&from_config).unwrap();
    }

    #[test]
    fn test_token_disk_parsing() {
        let from_disk = Config {
            vault: VaultConfig {
                namespace: None,
                server: "127.0.0.1:8200".to_string(),
                ca_certificate: None,
                token: Token::FromDisk(PathBuf::from("/token")),
                renew_ttl_secs: None,
                disable_cas: None,
                connection_timeout_ms: None,
                response_timeout_ms: None,
            },
        };

        let text_from_disk = r#"
vault:
    server: "127.0.0.1:8200"
    token:
        from_disk: "/token"
        "#;

        let de_from_disk: Config = serde_yaml::from_str(text_from_disk).unwrap();
        assert_eq!(de_from_disk, from_disk);
        // Just assert that it can be serialized, not about to do string comparison
        serde_yaml::to_string(&from_disk).unwrap();
    }

    #[test]
    fn test_token_reading() {
        let temppath = diem_temppath::TempPath::new();
        temppath.create_as_file().unwrap();
        let mut file = File::create(temppath.path()).unwrap();
        file.write_all(b"disk_token").unwrap();

        let disk = Token::FromDisk(temppath.path().to_path_buf());
        assert_eq!("disk_token", disk.read_token().unwrap());

        let config = Token::FromConfig("config_token".to_string());
        assert_eq!("config_token", config.read_token().unwrap());
    }
}
<|endoftext|>


<commit_before>use parking_lot::Mutex;
use std::mem;

use ctxt::VM;
use driver::cmd::Args;
use gc::marking;
use gc::root::{get_rootset, Slot};
use gc::space::Space;
use gc::tlab;
use gc::{fill_region, formatted_size, Address, CollectionStats, Collector, GcReason, Region, K};
use os;
use safepoint;
use timer::Timer;

pub struct SweepCollector {
    heap: Region,
    alloc: Mutex<SweepAllocator>,
    stats: Mutex<CollectionStats>,
}

impl SweepCollector {
    pub fn new(args: &Args) -> SweepCollector {
        let heap_size = args.max_heap_size();
        let ptr = os::mmap(heap_size, os::Writable);

        if ptr.is_null() {
            panic!("could not allocate heap of size {} bytes", heap_size);
        }

        let heap_start = Address::from_ptr(ptr);
        let heap_end = heap_start.offset(heap_size);
        let heap = Region::new(heap_start, heap_end);

        if args.flag_gc_verbose {
            println!("GC: {} {}", heap, formatted_size(heap_size));
        }

        SweepCollector {
            heap: heap,
            alloc: Mutex::new(SweepAllocator::new(heap)),
            stats: Mutex::new(CollectionStats::new()),
        }
    }
}

impl Collector for SweepCollector {
    fn supports_tlab(&self) -> bool {
        true
    }

    fn alloc_tlab_area(&self, vm: &VM, size: usize) -> Option<Region> {
        let ptr = self.inner_alloc(vm, size);

        if ptr.is_non_null() {
            return Some(ptr.region_start(size));
        }

        self.collect(vm, GcReason::AllocationFailure);

        let ptr = self.inner_alloc(vm, size);

        return if ptr.is_null() {
            None
        } else {
            Some(ptr.region_start(size))
        };
    }

    fn alloc(&self, vm: &VM, size: usize, _array_ref: bool) -> Address {
        let ptr = self.inner_alloc(vm, size);

        if ptr.is_non_null() {
            return ptr;
        }

        self.collect(vm, GcReason::AllocationFailure);
        self.inner_alloc(vm, size)
    }

    fn collect(&self, vm: &VM, reason: GcReason) {
        let mut timer = Timer::new(vm.args.flag_gc_stats);

        safepoint::stop_the_world(vm, |threads| {
            vm.perf_counters.stop();
            tlab::make_iterable_all(vm, threads);
            let rootset = get_rootset(vm, threads);
            self.mark_sweep(vm, &rootset, reason);
            vm.perf_counters.start();
        });

        if vm.args.flag_gc_stats {
            let duration = timer.stop();
            let mut stats = self.stats.lock();
            stats.add(duration);
        }
    }

    fn minor_collect(&self, vm: &VM, reason: GcReason) {
        self.collect(vm, reason);
    }

    fn dump_summary(&self, runtime: f32) {
        let stats = self.stats.lock();
        let (mutator, gc) = stats.percentage(runtime);

        println!("GC stats: total={:.1}", runtime);
        println!("GC stats: mutator={:.1}", stats.mutator(runtime));
        println!("GC stats: collection={:.1}", stats.pause());

        println!("");
        println!("GC stats: collection-count={}", stats.collections());
        println!("GC stats: collection-pauses={}", stats.pauses());

        println!(
            "GC summary: {:.1}ms collection ({}), {:.1}ms mutator, {:.1}ms total ({}% mutator, {}% GC)",
            stats.pause(),
            stats.collections(),
            stats.mutator(runtime),
            runtime,
            mutator,
            gc,
        );
    }
}

impl Drop for SweepCollector {
    fn drop(&mut self) {
        os::munmap(self.heap.start.to_ptr(), self.heap.size());
    }
}

impl SweepCollector {
    fn inner_alloc(&self, vm: &VM, size: usize) -> Address {
        let mut alloc = self.alloc.lock();
        alloc.allocate(vm, size)
    }

    fn mark_sweep(&self, vm: &VM, rootset: &[Slot], reason: GcReason) {
        let start = self.heap.start;
        let top = self.alloc.lock().top;

        let mut collector = MarkSweep {
            vm: vm,
            heap: Region::new(start, top),
            perm_space: &vm.gc.perm_space,

            rootset: rootset,
            reason: reason,
            free_list: FreeList::new(),
        };

        collector.collect();

        let mut alloc = self.alloc.lock();
        alloc.free_list = mem::replace(&mut collector.free_list, FreeList::new());
    }
}

struct MarkSweep<'a, 'ast: 'a> {
    vm: &'a VM<'ast>,
    heap: Region,
    perm_space: &'a Space,

    rootset: &'a [Slot],
    reason: GcReason,
    free_list: FreeList,
}

impl<'a, 'ast> MarkSweep<'a, 'ast> {
    fn collect(&mut self) {
        self.mark();
        self.sweep();
    }

    fn mark(&mut self) {
        marking::start(self.rootset, self.heap, self.perm_space.total());
    }

    fn sweep(&mut self) {
        let start = self.heap.start;
        let end = self.heap.end;

        let mut scan = start;
        let mut garbage_start = Address::null();

        while scan < end {
            let object = scan.to_mut_obj();

            if object.header().vtblptr().is_null() {
                scan = scan.add_ptr(1);
                continue;
            }

            let object_size = object.size();

            if object.header().is_marked_non_atomic() {
                self.free(garbage_start, scan);
                garbage_start = Address::null();
                object.header_mut().unmark_non_atomic();
            } else if garbage_start.is_non_null() {
                // more garbage, do nothing
            } else {
                // start garbage, last object was live
                garbage_start = scan;
            }

            scan = scan.offset(object_size);
        }

        self.free(garbage_start, end);
    }

    fn free(&mut self, start: Address, end: Address) {
        if start.is_null() {
            return;
        }

        fill_region(self.vm, start, end);

        let size = end.offset_from(start);
        self.free_list.add(start, size);
    }
}

struct SweepAllocator {
    top: Address,
    limit: Address,
    free_list: FreeList,
}

impl SweepAllocator {
    fn new(heap: Region) -> SweepAllocator {
        SweepAllocator {
            top: heap.start,
            limit: heap.end,
            free_list: FreeList::new(),
        }
    }

    fn allocate(&mut self, vm: &VM, size: usize) -> Address {
        let object = self.top;
        let next_top = object.offset(size);

        if next_top <= self.limit {
            self.top = next_top;
            return object;
        }

        let free_space = self.free_list.alloc(size);

        if free_space.is_non_null() {
            let object = free_space.addr();
            let free_size = free_space.size();
            assert!(size <= free_size);

            let free_start = object.offset(size);
            let free_end = object.offset(free_size);
            let new_free_size = free_end.offset_from(free_start);

            fill_region(vm, free_start, free_end);
            self.free_list.add(free_start, new_free_size);
            return object;
        }

        Address::null()
    }
}

pub const SIZE_CLASSES: usize = 6;

pub const SIZE_CLASS_SMALLEST: SizeClass = SizeClass(0);
pub const SIZE_SMALLEST: usize = 16;

pub const SIZE_CLASS_TINY: SizeClass = SizeClass(1);
pub const SIZE_TINY: usize = 32;

pub const SIZE_CLASS_SMALL: SizeClass = SizeClass(2);
pub const SIZE_SMALL: usize = 128;

pub const SIZE_CLASS_MEDIUM: SizeClass = SizeClass(3);
pub const SIZE_MEDIUM: usize = 2 * K;

pub const SIZE_CLASS_LARGE: SizeClass = SizeClass(4);
pub const SIZE_LARGE: usize = 8 * K;

pub const SIZE_CLASS_HUGE: SizeClass = SizeClass(5);
pub const SIZE_HUGE: usize = 32 * K;

#[derive(Copy, Clone, PartialEq, Eq)]
pub struct SizeClass(usize);

pub const SIZES: [usize; SIZE_CLASSES] = [
    SIZE_SMALLEST,
    SIZE_TINY,
    SIZE_SMALL,
    SIZE_MEDIUM,
    SIZE_LARGE,
    SIZE_HUGE,
];

impl SizeClass {
    fn from_size(size: usize) -> SizeClass {
        assert!(size >= SIZE_SMALLEST);

        if size <= SIZE_SMALLEST {
            SIZE_CLASS_SMALLEST
        } else if size <= SIZE_TINY {
            SIZE_CLASS_TINY
        } else if size <= SIZE_SMALL {
            SIZE_CLASS_SMALL
        } else if size <= SIZE_MEDIUM {
            SIZE_CLASS_MEDIUM
        } else if size <= SIZE_LARGE {
            SIZE_CLASS_LARGE
        } else {
            SIZE_CLASS_HUGE
        }
    }

    fn idx(self) -> usize {
        self.0
    }

    fn size(self) -> usize {
        SIZES[self.0]
    }
}

struct FreeList {
    classes: Vec<FreeListClass>,
}

impl FreeList {
    fn new() -> FreeList {
        let mut classes = Vec::with_capacity(SIZE_CLASSES);

        for _ in 0..SIZE_CLASSES {
            classes.push(FreeListClass::new());
        }

        FreeList { classes: classes }
    }

    fn add(&mut self, addr: Address, size: usize) {
        if size < SIZE_SMALLEST {
            return;
        }

        debug_assert!(size >= SIZE_SMALLEST);
        let szclass = SizeClass::from_size(size);
        self.classes[szclass.idx()].add(FreeSpace(addr));
    }

    fn alloc(&mut self, size: usize) -> FreeSpace {
        let szclass = SizeClass::from_size(size).idx();
        let last = SIZE_CLASS_HUGE.idx();

        for class in szclass..last {
            let result = self.classes[class].first();

            if result.addr().is_non_null() {
                return result;
            }
        }

        self.classes[SIZE_CLASS_HUGE.idx()].find(size)
    }
}

struct FreeListClass(Vec<FreeSpace>);

impl FreeListClass {
    fn new() -> FreeListClass {
        FreeListClass(Vec::new())
    }

    fn add(&mut self, addr: FreeSpace) {
        self.0.push(addr);
    }

    fn first(&mut self) -> FreeSpace {
        if let Some(space) = self.0.pop() {
            space
        } else {
            FreeSpace::null()
        }
    }

    fn find(&mut self, minimum_size: usize) -> FreeSpace {
        let len = self.0.len();
        for idx in 0..len {
            let curr = self.0[idx];
            if curr.size() >= minimum_size {
                self.0.remove(idx);
                return curr;
            }
        }

        FreeSpace::null()
    }
}

#[derive(Copy, Clone)]
struct FreeSpace(Address);

impl FreeSpace {
    #[inline(always)]
    fn null() -> FreeSpace {
        FreeSpace(Address::null())
    }

    #[inline(always)]
    fn is_null(self) -> bool {
        self.addr().is_null()
    }

    #[inline(always)]
    fn is_non_null(self) -> bool {
        self.addr().is_non_null()
    }

    #[inline(always)]
    fn addr(self) -> Address {
        self.0
    }

    #[inline(always)]
    fn size(self) -> usize {
        let obj = self.addr().to_obj();
        obj.size() as usize
    }
}
<commit_msg>sweep: round up or down to next size class<commit_after>use parking_lot::Mutex;
use std::mem;

use ctxt::VM;
use driver::cmd::Args;
use gc::marking;
use gc::root::{get_rootset, Slot};
use gc::space::Space;
use gc::tlab;
use gc::{fill_region, formatted_size, Address, CollectionStats, Collector, GcReason, Region, K};
use os;
use safepoint;
use timer::Timer;

pub struct SweepCollector {
    heap: Region,
    alloc: Mutex<SweepAllocator>,
    stats: Mutex<CollectionStats>,
}

impl SweepCollector {
    pub fn new(args: &Args) -> SweepCollector {
        let heap_size = args.max_heap_size();
        let ptr = os::mmap(heap_size, os::Writable);

        if ptr.is_null() {
            panic!("could not allocate heap of size {} bytes", heap_size);
        }

        let heap_start = Address::from_ptr(ptr);
        let heap_end = heap_start.offset(heap_size);
        let heap = Region::new(heap_start, heap_end);

        if args.flag_gc_verbose {
            println!("GC: {} {}", heap, formatted_size(heap_size));
        }

        SweepCollector {
            heap: heap,
            alloc: Mutex::new(SweepAllocator::new(heap)),
            stats: Mutex::new(CollectionStats::new()),
        }
    }
}

impl Collector for SweepCollector {
    fn supports_tlab(&self) -> bool {
        true
    }

    fn alloc_tlab_area(&self, vm: &VM, size: usize) -> Option<Region> {
        let ptr = self.inner_alloc(vm, size);

        if ptr.is_non_null() {
            return Some(ptr.region_start(size));
        }

        self.collect(vm, GcReason::AllocationFailure);

        let ptr = self.inner_alloc(vm, size);

        return if ptr.is_null() {
            None
        } else {
            Some(ptr.region_start(size))
        };
    }

    fn alloc(&self, vm: &VM, size: usize, _array_ref: bool) -> Address {
        let ptr = self.inner_alloc(vm, size);

        if ptr.is_non_null() {
            return ptr;
        }

        self.collect(vm, GcReason::AllocationFailure);
        self.inner_alloc(vm, size)
    }

    fn collect(&self, vm: &VM, reason: GcReason) {
        let mut timer = Timer::new(vm.args.flag_gc_stats);

        safepoint::stop_the_world(vm, |threads| {
            vm.perf_counters.stop();
            tlab::make_iterable_all(vm, threads);
            let rootset = get_rootset(vm, threads);
            self.mark_sweep(vm, &rootset, reason);
            vm.perf_counters.start();
        });

        if vm.args.flag_gc_stats {
            let duration = timer.stop();
            let mut stats = self.stats.lock();
            stats.add(duration);
        }
    }

    fn minor_collect(&self, vm: &VM, reason: GcReason) {
        self.collect(vm, reason);
    }

    fn dump_summary(&self, runtime: f32) {
        let stats = self.stats.lock();
        let (mutator, gc) = stats.percentage(runtime);

        println!("GC stats: total={:.1}", runtime);
        println!("GC stats: mutator={:.1}", stats.mutator(runtime));
        println!("GC stats: collection={:.1}", stats.pause());

        println!("");
        println!("GC stats: collection-count={}", stats.collections());
        println!("GC stats: collection-pauses={}", stats.pauses());

        println!(
            "GC summary: {:.1}ms collection ({}), {:.1}ms mutator, {:.1}ms total ({}% mutator, {}% GC)",
            stats.pause(),
            stats.collections(),
            stats.mutator(runtime),
            runtime,
            mutator,
            gc,
        );
    }
}

impl Drop for SweepCollector {
    fn drop(&mut self) {
        os::munmap(self.heap.start.to_ptr(), self.heap.size());
    }
}

impl SweepCollector {
    fn inner_alloc(&self, vm: &VM, size: usize) -> Address {
        let mut alloc = self.alloc.lock();
        alloc.allocate(vm, size)
    }

    fn mark_sweep(&self, vm: &VM, rootset: &[Slot], reason: GcReason) {
        let start = self.heap.start;
        let top = self.alloc.lock().top;

        let mut collector = MarkSweep {
            vm: vm,
            heap: Region::new(start, top),
            perm_space: &vm.gc.perm_space,

            rootset: rootset,
            reason: reason,
            free_list: FreeList::new(),
        };

        collector.collect();

        let mut alloc = self.alloc.lock();
        alloc.free_list = mem::replace(&mut collector.free_list, FreeList::new());
    }
}

struct MarkSweep<'a, 'ast: 'a> {
    vm: &'a VM<'ast>,
    heap: Region,
    perm_space: &'a Space,

    rootset: &'a [Slot],
    reason: GcReason,
    free_list: FreeList,
}

impl<'a, 'ast> MarkSweep<'a, 'ast> {
    fn collect(&mut self) {
        self.mark();
        self.sweep();
    }

    fn mark(&mut self) {
        marking::start(self.rootset, self.heap, self.perm_space.total());
    }

    fn sweep(&mut self) {
        let start = self.heap.start;
        let end = self.heap.end;

        let mut scan = start;
        let mut garbage_start = Address::null();

        while scan < end {
            let object = scan.to_mut_obj();

            if object.header().vtblptr().is_null() {
                scan = scan.add_ptr(1);
                continue;
            }

            let object_size = object.size();

            if object.header().is_marked_non_atomic() {
                self.free(garbage_start, scan);
                garbage_start = Address::null();
                object.header_mut().unmark_non_atomic();
            } else if garbage_start.is_non_null() {
                // more garbage, do nothing
            } else {
                // start garbage, last object was live
                garbage_start = scan;
            }

            scan = scan.offset(object_size);
        }

        assert!(scan == end);
        self.free(garbage_start, end);
    }

    fn free(&mut self, start: Address, end: Address) {
        if start.is_null() {
            return;
        }

        fill_region(self.vm, start, end);

        let size = end.offset_from(start);
        self.free_list.add(start, size);
    }
}

struct SweepAllocator {
    top: Address,
    limit: Address,
    free_list: FreeList,
}

impl SweepAllocator {
    fn new(heap: Region) -> SweepAllocator {
        SweepAllocator {
            top: heap.start,
            limit: heap.end,
            free_list: FreeList::new(),
        }
    }

    fn allocate(&mut self, vm: &VM, size: usize) -> Address {
        let object = self.top;
        let next_top = object.offset(size);

        if next_top <= self.limit {
            self.top = next_top;
            return object;
        }

        let free_space = self.free_list.alloc(size);

        if free_space.is_non_null() {
            let object = free_space.addr();
            let free_size = free_space.size();
            assert!(size <= free_size);

            let free_start = object.offset(size);
            let free_end = object.offset(free_size);
            let new_free_size = free_end.offset_from(free_start);

            fill_region(vm, free_start, free_end);
            self.free_list.add(free_start, new_free_size);
            return object;
        }

        Address::null()
    }
}

pub const SIZE_CLASSES: usize = 6;

pub const SIZE_CLASS_SMALLEST: SizeClass = SizeClass(0);
pub const SIZE_SMALLEST: usize = 16;

pub const SIZE_CLASS_TINY: SizeClass = SizeClass(1);
pub const SIZE_TINY: usize = 32;

pub const SIZE_CLASS_SMALL: SizeClass = SizeClass(2);
pub const SIZE_SMALL: usize = 128;

pub const SIZE_CLASS_MEDIUM: SizeClass = SizeClass(3);
pub const SIZE_MEDIUM: usize = 2 * K;

pub const SIZE_CLASS_LARGE: SizeClass = SizeClass(4);
pub const SIZE_LARGE: usize = 8 * K;

pub const SIZE_CLASS_HUGE: SizeClass = SizeClass(5);
pub const SIZE_HUGE: usize = 32 * K;

#[derive(Copy, Clone, PartialEq, Eq)]
pub struct SizeClass(usize);

pub const SIZES: [usize; SIZE_CLASSES] = [
    SIZE_SMALLEST,
    SIZE_TINY,
    SIZE_SMALL,
    SIZE_MEDIUM,
    SIZE_LARGE,
    SIZE_HUGE,
];

impl SizeClass {
    fn next_up(size: usize) -> SizeClass {
        assert!(size >= SIZE_SMALLEST);

        if size <= SIZE_SMALLEST {
            SIZE_CLASS_SMALLEST
        } else if size <= SIZE_TINY {
            SIZE_CLASS_TINY
        } else if size <= SIZE_SMALL {
            SIZE_CLASS_SMALL
        } else if size <= SIZE_MEDIUM {
            SIZE_CLASS_MEDIUM
        } else if size <= SIZE_LARGE {
            SIZE_CLASS_LARGE
        } else {
            SIZE_CLASS_HUGE
        }
    }

    fn next_down(size: usize) -> SizeClass {
        assert!(size >= SIZE_SMALLEST);

        if size < SIZE_TINY {
            SIZE_CLASS_SMALLEST
        } else if size < SIZE_SMALL {
            SIZE_CLASS_TINY
        } else if size < SIZE_MEDIUM {
            SIZE_CLASS_SMALL
        } else if size < SIZE_LARGE {
            SIZE_CLASS_MEDIUM
        } else if size < SIZE_HUGE {
            SIZE_CLASS_LARGE
        } else {
            SIZE_CLASS_HUGE
        }
    }

    fn idx(self) -> usize {
        self.0
    }

    fn size(self) -> usize {
        SIZES[self.0]
    }
}

struct FreeList {
    classes: Vec<FreeListClass>,
}

impl FreeList {
    fn new() -> FreeList {
        let mut classes = Vec::with_capacity(SIZE_CLASSES);

        for _ in 0..SIZE_CLASSES {
            classes.push(FreeListClass::new());
        }

        FreeList { classes: classes }
    }

    fn add(&mut self, addr: Address, size: usize) {
        if size < SIZE_SMALLEST {
            return;
        }

        debug_assert!(size >= SIZE_SMALLEST);
        let szclass = SizeClass::next_down(size);
        self.classes[szclass.idx()].add(FreeSpace(addr));
    }

    fn alloc(&mut self, size: usize) -> FreeSpace {
        let szclass = SizeClass::next_up(size).idx();
        let last = SIZE_CLASS_HUGE.idx();

        for class in szclass..last {
            let result = self.classes[class].first();

            if result.is_non_null() {
                if result.size() < size {
                    let class = SizeClass(class);
                    println!("class={} class.size={} actual={} expected={}", class.0, class.size(), result.size(), size);
                }
                assert!(result.size() >= size);
                return result;
            }
        }

        self.classes[SIZE_CLASS_HUGE.idx()].find(size)
    }
}

struct FreeListClass(Vec<FreeSpace>);

impl FreeListClass {
    fn new() -> FreeListClass {
        FreeListClass(Vec::new())
    }

    fn add(&mut self, addr: FreeSpace) {
        self.0.push(addr);
    }

    fn first(&mut self) -> FreeSpace {
        if let Some(space) = self.0.pop() {
            space
        } else {
            FreeSpace::null()
        }
    }

    fn find(&mut self, minimum_size: usize) -> FreeSpace {
        let len = self.0.len();
        for idx in 0..len {
            let curr = self.0[idx];
            if curr.size() >= minimum_size {
                self.0.remove(idx);
                return curr;
            }
        }

        FreeSpace::null()
    }
}

#[derive(Copy, Clone)]
struct FreeSpace(Address);

impl FreeSpace {
    #[inline(always)]
    fn null() -> FreeSpace {
        FreeSpace(Address::null())
    }

    #[inline(always)]
    fn is_null(self) -> bool {
        self.addr().is_null()
    }

    #[inline(always)]
    fn is_non_null(self) -> bool {
        self.addr().is_non_null()
    }

    #[inline(always)]
    fn addr(self) -> Address {
        self.0
    }

    #[inline(always)]
    fn size(self) -> usize {
        let obj = self.addr().to_obj();
        obj.size() as usize
    }
}
<|endoftext|>


<commit_before>use std::io::prelude::*;
use std::fs::File;
use std::cell::RefCell;
use std::collections::{HashSet, HashMap};

use syntax::ast::*;
use syntax::print;
use syntax::ptr::P;
use syntax::ext;
use syntax::ext::base::ExtCtxt;
use syntax::ext::build::AstBuilder;
use syntax::parse::token::{str_to_ident, Token, IdentStyle};
use syntax::codemap::{ExpnId, ExpnInfo, CompilerExpansion, NameAndSpan, DUMMY_SP};

use rustc::session::{build_session,Session};
use rustc::session::config::{build_session_options,build_configuration, Input};
use rustc_driver::{handle_options,diagnostics_registry};
use rustc_driver::driver::{phase_1_parse_input, source_name};

use super::state::State;

const CRATE_SRC : &'static str =  "//Generated by hsm-gen, modifications will be lost when regenerating
                                   use super::shared_data::SharedData;
                                   use super::hsm_functions;";

pub struct HsmGenerator {
    sess        : Session,
    cfg         : CrateConfig,
    src_name    : String,
    krate       : RefCell<Crate>
}
impl HsmGenerator {
    pub fn new() -> Self {
        let matches = match handle_options(vec!["".to_string(), "-".to_string()]) {
            Some(matches) => matches,
            None => unreachable!()
        };
        let descriptions = diagnostics_registry();
        let sopts = build_session_options(&matches);
        let sess = build_session(sopts, None, descriptions);
        let cfg = build_configuration(&sess);
        let input = Input::Str(CRATE_SRC.to_string());
        let src_name = source_name(&input);
        let krate = RefCell::new(phase_1_parse_input(&sess, cfg.clone(), &input));

        HsmGenerator {
            sess    : sess,
            cfg     : cfg,
            src_name: src_name,
            krate   : krate
        }
    }

    fn ecfg(&self) -> ext::expand::ExpansionConfig {
        ext::expand::ExpansionConfig {
            crate_name      : self.src_name.clone(),
            features        : None, //Some(&self.sess.features.borrow()),
            recursion_limit : self.sess.recursion_limit.get(),
            trace_mac       : self.sess.opts.debugging_opts.trace_macros,
        }
    }
    fn extctxt(&self) -> ExtCtxt {
        let mut cx = ExtCtxt::new(&self.sess.parse_sess, self.cfg.clone(), self.ecfg());
        cx.backtrace = ExpnId::from_u32(0);
        cx.codemap().record_expansion(
            ExpnInfo {
                call_site: self.krate.borrow().span,
                callee: NameAndSpan {
                    name: "mattis".to_string(),
                    format: CompilerExpansion,
                    allow_internal_unstable: true,
                    span: None,
                },
            }
        );
        cx
    }

    pub fn print(&self, file: &str) {
        let src_nam = self.src_name.clone();
        let src = self.sess.codemap().get_filemap(&src_nam[..])
                            .src
                            .as_ref()
                            .unwrap()
                            .as_bytes()
                            .to_vec();
        let mut rdr = &src[..];
        let mut out = Vec::new();
        let ann = print::pprust::NoAnn;
        {
            let out_w: &mut Write = &mut out;
            print::pprust::print_crate( self.sess.codemap(),
                                        self.sess.diagnostic(),
                                        &self.krate.borrow(),
                                        src_nam,
                                        &mut rdr,
                                        box out_w,
                                        &ann,
                                        false).expect("Could not format crate");
        }
        // print!("{}", String::from_utf8(out).unwrap());
        let mut f = File::create(file).expect("Could not create file");
        f.write(&out).expect("Could not write to file");
    }

    pub fn create_event_enum(&self, hs: HashSet<String>) {
        let cx = self.extctxt();
        let mut variants: Vec<P<Variant>> = Vec::new();
        for var_name in &hs {
            variants.push(P(cx.variant(
                DUMMY_SP,
                str_to_ident(var_name),
                Vec::new()
            )));
        }
        let en = cx.item_enum(
            DUMMY_SP,
            str_to_ident("Events"),
            EnumDef{ variants: variants }
        );
        self.krate.borrow_mut().module.items.push(en);
    }

    pub fn create_hsm_objects(&self, states: HashSet<String>) {
        let cx = self.extctxt();
        let events = str_to_ident("Events");
        let st_str = str_to_ident("StateStruct");
        let st     = str_to_ident("States");
        let shr_dat= str_to_ident("SharedData");
        let states: Vec<TokenTree> = states
            .iter()
            .map(|st_nam| vec![Token::Ident(str_to_ident(st_nam), IdentStyle::Plain)])
            .collect::<Vec<_>>()
            .join(&Token::Comma)
            .into_iter()
            .map(|t| TtToken(DUMMY_SP, t))
            .collect();

        let x = quote_item!(&cx,
            hsm_define_objects!($st_str, $st, $events, $shr_dat, (
                $states
            ));
        ).unwrap();
        self.krate.borrow_mut().module.items.push(x);
    }

    pub fn create_state_impls(&self, hm: HashMap<String, State>) {
        let events = str_to_ident("Events");
        let states = str_to_ident("States");
        let shr_dat= str_to_ident("SharedData");
        for state in hm.values() {
            self.create_state_impl(state, &events, &states, &shr_dat);
        }
    }

    fn create_state_impl(&self, state: &State, events: &Ident, states: &Ident, shr_dat: &Ident) {
        let cx = self.extctxt();
        let state_ident = str_to_ident(state.name.as_ref().unwrap());
        let mut arms: Vec<Arm> = Vec::new();
        if let Some(ref func_nam) = state.entry {
            let func_ident = str_to_ident(func_nam);
            arms.push(cx.arm(DUMMY_SP,
                             vec!(quote_pat!(&cx, hsm::Event::Enter)),
                             quote_expr!(&cx, hsm_functions::$func_ident(self, shr_data, evt, probe))
            ));
        }
        if let Some(ref func_nam) = state.exit {
            let func_ident = str_to_ident(func_nam);
            arms.push(cx.arm(DUMMY_SP,
                             vec!(quote_pat!(&cx, hsm::Event::Exit)),
                             quote_expr!(&cx, hsm_functions::$func_ident(self, shr_data, evt, probe))
            ));
        }
        for (trigger, target) in state.transitions.iter() {
            let target_ident = str_to_ident(target);
            let trigger_ident = str_to_ident(trigger);
            arms.push(cx.arm(DUMMY_SP,
                             vec!(quote_pat!(&cx, hsm::Event($trigger_ident))),
                             quote_expr!(&cx, hsm::Action::Transition($states::$target_ident))
            ));
        };
        let match_expr = cx.expr_match(DUMMY_SP, quote_expr!(&cx, evt), arms);
        let x = quote_item!(&cx,
            impl hsm::State<$events, $states, $shr_dat> for $state_ident<$events, $states, $shr_dat> {
                fn handle_event(&mut self, shr_data: &mut $shr_dat, evt: hsm::Event<$events>, probe: bool) -> hsm::Action<$states> {
                    $match_expr
                }
            }
        ).unwrap();
        self.krate.borrow_mut().module.items.push(x);
    }
}
<commit_msg>fix impl for enter/exit events and use delayed transitions<commit_after>use std::io::prelude::*;
use std::fs::File;
use std::cell::RefCell;
use std::collections::{HashSet, HashMap};

use syntax::ast::*;
use syntax::print;
use syntax::ptr::P;
use syntax::ext;
use syntax::ext::base::ExtCtxt;
use syntax::ext::build::AstBuilder;
use syntax::parse::token::{str_to_ident, Token, IdentStyle};
use syntax::codemap::{ExpnId, ExpnInfo, CompilerExpansion, NameAndSpan, DUMMY_SP};

use rustc::session::{build_session,Session};
use rustc::session::config::{build_session_options,build_configuration, Input};
use rustc_driver::{handle_options,diagnostics_registry};
use rustc_driver::driver::{phase_1_parse_input, source_name};

use super::state::State;

const CRATE_SRC : &'static str =  "//Generated by hsm-gen, modifications will be lost when regenerating
                                   use super::shared_data::SharedData;
                                   use super::hsm_functions;";

pub struct HsmGenerator {
    sess        : Session,
    cfg         : CrateConfig,
    src_name    : String,
    krate       : RefCell<Crate>
}
impl HsmGenerator {
    pub fn new() -> Self {
        let matches = match handle_options(vec!["".to_string(), "-".to_string()]) {
            Some(matches) => matches,
            None => unreachable!()
        };
        let descriptions = diagnostics_registry();
        let sopts = build_session_options(&matches);
        let sess = build_session(sopts, None, descriptions);
        let cfg = build_configuration(&sess);
        let input = Input::Str(CRATE_SRC.to_string());
        let src_name = source_name(&input);
        let krate = RefCell::new(phase_1_parse_input(&sess, cfg.clone(), &input));

        HsmGenerator {
            sess    : sess,
            cfg     : cfg,
            src_name: src_name,
            krate   : krate
        }
    }

    fn ecfg(&self) -> ext::expand::ExpansionConfig {
        ext::expand::ExpansionConfig {
            crate_name      : self.src_name.clone(),
            features        : None, //Some(&self.sess.features.borrow()),
            recursion_limit : self.sess.recursion_limit.get(),
            trace_mac       : self.sess.opts.debugging_opts.trace_macros,
        }
    }
    fn extctxt(&self) -> ExtCtxt {
        let mut cx = ExtCtxt::new(&self.sess.parse_sess, self.cfg.clone(), self.ecfg());
        cx.backtrace = ExpnId::from_u32(0);
        cx.codemap().record_expansion(
            ExpnInfo {
                call_site: self.krate.borrow().span,
                callee: NameAndSpan {
                    name: "mattis".to_string(),
                    format: CompilerExpansion,
                    allow_internal_unstable: true,
                    span: None,
                },
            }
        );
        cx
    }

    pub fn print(&self, file: &str) {
        let src_nam = self.src_name.clone();
        let src = self.sess.codemap().get_filemap(&src_nam[..])
                            .src
                            .as_ref()
                            .unwrap()
                            .as_bytes()
                            .to_vec();
        let mut rdr = &src[..];
        let mut out = Vec::new();
        let ann = print::pprust::NoAnn;
        {
            let out_w: &mut Write = &mut out;
            print::pprust::print_crate( self.sess.codemap(),
                                        self.sess.diagnostic(),
                                        &self.krate.borrow(),
                                        src_nam,
                                        &mut rdr,
                                        box out_w,
                                        &ann,
                                        false).expect("Could not format crate");
        }
        // print!("{}", String::from_utf8(out).unwrap());
        let mut f = File::create(file).expect("Could not create file");
        f.write(&out).expect("Could not write to file");
    }

    pub fn create_event_enum(&self, hs: HashSet<String>) {
        let cx = self.extctxt();
        let mut variants: Vec<P<Variant>> = Vec::new();
        for var_name in &hs {
            variants.push(P(cx.variant(
                DUMMY_SP,
                str_to_ident(var_name),
                Vec::new()
            )));
        }
        let en = cx.item_enum(
            DUMMY_SP,
            str_to_ident("Events"),
            EnumDef{ variants: variants }
        );
        self.krate.borrow_mut().module.items.push(en);
    }

    pub fn create_hsm_objects(&self, states: HashSet<String>) {
        let cx = self.extctxt();
        let events = str_to_ident("Events");
        let st_str = str_to_ident("StateStruct");
        let st     = str_to_ident("States");
        let shr_dat= str_to_ident("SharedData");
        let states: Vec<TokenTree> = states
            .iter()
            .map(|st_nam| vec![Token::Ident(str_to_ident(st_nam), IdentStyle::Plain)])
            .collect::<Vec<_>>()
            .join(&Token::Comma)
            .into_iter()
            .map(|t| TtToken(DUMMY_SP, t))
            .collect();

        let x = quote_item!(&cx,
            hsm_define_objects!($st_str, $st, $events, $shr_dat, (
                $states
            ));
        ).unwrap();
        self.krate.borrow_mut().module.items.push(x);
    }

    pub fn create_state_impls(&self, hm: HashMap<String, State>) {
        let events = str_to_ident("Events");
        let states = str_to_ident("States");
        let shr_dat= str_to_ident("SharedData");
        for state in hm.values() {
            self.create_state_impl(state, &events, &states, &shr_dat);
        }
    }

    fn create_enter_exit_arm(&self, cx: &ExtCtxt, opt_func: &Option<String>, evt_type: &str) -> Option<Arm> {
        if let &Some(ref func_nam) = opt_func {
            let func_ident = str_to_ident(func_nam);
            let evt_ident = str_to_ident(evt_type);
            Some(cx.arm(DUMMY_SP,
                   vec!(quote_pat!(&cx, hsm::Event::$evt_ident)),
                   quote_expr!(&cx, {
                        hsm_functions::$func_ident(self, shr_data, evt, probe);
                        hsm::Action::Ignore
                   })
            ))
        } else { None }
    }

    fn create_state_impl(&self, state: &State, events: &Ident, states: &Ident, shr_dat: &Ident) {
        let cx = self.extctxt();
        let state_ident = str_to_ident(state.name.as_ref().unwrap());
        let mut arms: Vec<Arm> = Vec::new();
        self.create_enter_exit_arm(&cx, &state.entry, "Enter").and_then(|x| {arms.push(x); Some(1)});
        self.create_enter_exit_arm(&cx, &state.exit, "Exit").and_then(|x| {arms.push(x); Some(1)});
        for (trigger, target) in state.transitions.iter() {
            let target_ident = str_to_ident(target);
            let trigger_ident = str_to_ident(trigger);
            let pat = if trigger == "_" {
                quote_pat!(&cx, $trigger_ident)
            } else {
                quote_pat!(&cx, hsm::Event($trigger_ident))
            };
            let trans = quote_expr!(&cx, hsm::Action::Transition($states::$target_ident));
            arms.push(cx.arm(DUMMY_SP,
                             vec!(pat),
                             quote_expr!(&cx, hsm_delayed_transition!(probe, { $trans }))
            ));
        };
        if !state.transitions.contains_key("_") {
            arms.push(cx.arm(DUMMY_SP,
                             vec!(quote_pat!(&cx, _)),
                             quote_expr!(&cx, hsm::Action::Parent)
            ));
        };
        let match_expr = cx.expr_match(DUMMY_SP, quote_expr!(&cx, evt), arms);
        let x = quote_item!(&cx,
            impl hsm::State<$events, $states, $shr_dat> for $state_ident<$events, $states, $shr_dat> {
                fn handle_event(&mut self, shr_data: &mut $shr_dat, evt: hsm::Event<$events>, probe: bool) -> hsm::Action<$states> {
                    $match_expr
                }
            }
        ).unwrap();
        self.krate.borrow_mut().module.items.push(x);
    }
}
<|endoftext|>


<commit_before>use super::*;

#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct Generics {
    pub lifetimes: Vec<LifetimeDef>,
    pub ty_params: Vec<TyParam>,
    pub where_clause: Vec<WherePredicate>,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Lifetime {
    pub ident: Ident,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct LifetimeDef {
    pub lifetime: Lifetime,
    pub bounds: Vec<Lifetime>,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct TyParam {
    pub ident: Ident,
    pub bounds: Vec<TyParamBound>,
    pub default: Option<Ty>,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum TyParamBound {
    MaybeSized,
    Region(Lifetime),
    Trait(PolyTraitRef),
}

/// A single predicate in a `where` clause
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum WherePredicate {
    /// A type binding, e.g. `for<'c> Foo: Send+Clone+'c`
    BoundPredicate(WhereBoundPredicate),
    /// A lifetime predicate, e.g. `'a: 'b+'c`
    RegionPredicate(WhereRegionPredicate),
}

/// A type bound.
///
/// E.g. `for<'c> Foo: Send+Clone+'c`
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct WhereBoundPredicate {
    /// Any lifetimes from a `for` binding
    pub bound_lifetimes: Vec<LifetimeDef>,
    /// The type being bounded
    pub bounded_ty: Ty,
    /// Trait and lifetime bounds (`Clone+Send+'static`)
    pub bounds: Vec<TyParamBound>,
}

/// A lifetime predicate.
///
/// E.g. `'a: 'b+'c`
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct WhereRegionPredicate {
    pub lifetime: Lifetime,
    pub bounds: Vec<Lifetime>,
}

#[cfg(feature = "parsing")]
pub mod parsing {
    use super::*;
    use common::parsing::word;
    use ty::parsing::{ty, poly_trait_ref};
    use nom::multispace;

    named!(pub generics<&str, Generics>, do_parse!(
        bracketed: alt!(
            do_parse!(
                punct!("<") >>
                lifetimes: separated_list!(punct!(","), lifetime_def) >>
                ty_params: opt_vec!(preceded!(
                    cond!(!lifetimes.is_empty(), punct!(",")),
                    separated_nonempty_list!(punct!(","), ty_param)
                )) >>
                punct!(">") >>
                (lifetimes, ty_params)
            )
            |
            epsilon!() => { |_| (Vec::new(), Vec::new()) }
        ) >>
        where_clause: opt_vec!(do_parse!(
            punct!("where") >>
            multispace >>
            predicates: separated_nonempty_list!(punct!(","), where_predicate) >>
            opt!(punct!(",")) >>
            (predicates)
        )) >>
        (Generics {
            lifetimes: bracketed.0,
            ty_params: bracketed.1,
            where_clause: where_clause,
        })
    ));

    named!(pub lifetime<&str, Lifetime>, preceded!(
        punct!("'"),
        map!(word, |ident| Lifetime {
            ident: format!("'{}", ident).into(),
        })
    ));

    named!(pub lifetime_def<&str, LifetimeDef>, do_parse!(
        life: lifetime >>
        bounds: opt_vec!(preceded!(
            punct!(":"),
            separated_nonempty_list!(punct!(","), lifetime)
        )) >>
        (LifetimeDef {
            lifetime: life,
            bounds: bounds,
        })
    ));

    named!(pub bound_lifetimes<&str, Vec<LifetimeDef> >, opt_vec!(do_parse!(
        punct!("for") >>
        punct!("<") >>
        lifetimes: separated_list!(punct!(","), lifetime_def) >>
        punct!(">") >>
        (lifetimes)
    )));

    named!(ty_param<&str, TyParam>, do_parse!(
        ident: word >>
        bounds: opt_vec!(preceded!(
            punct!(":"),
            separated_nonempty_list!(punct!("+"), ty_param_bound)
        )) >>
        default: opt!(preceded!(
            punct!("="),
            ty
        )) >>
        (TyParam {
            ident: ident,
            bounds: bounds,
            default: default,
        })
    ));

    named!(pub ty_param_bound<&str, TyParamBound>, alt!(
        tuple!(punct!("?"), punct!("Sized")) => { |_| TyParamBound::MaybeSized }
        |
        lifetime => { TyParamBound::Region }
        |
        poly_trait_ref => { TyParamBound::Trait }
    ));

    named!(where_predicate<&str, WherePredicate>, alt!(
        do_parse!(
            ident: lifetime >>
            punct!(":") >>
            bounds: separated_nonempty_list!(punct!("+"), lifetime) >>
            (WherePredicate::RegionPredicate(WhereRegionPredicate {
                lifetime: ident,
                bounds: bounds,
            }))
        )
        |
        do_parse!(
            bound_lifetimes: bound_lifetimes >>
            bounded_ty: ty >>
            punct!(":") >>
            bounds: separated_nonempty_list!(punct!("+"), ty_param_bound) >>
            (WherePredicate::BoundPredicate(WhereBoundPredicate {
                bound_lifetimes: bound_lifetimes,
                bounded_ty: bounded_ty,
                bounds: bounds,
            }))
        )
    ));
}

#[cfg(feature = "printing")]
mod printing {
    use super::*;
    use quote::{Tokens, ToTokens};

    impl ToTokens for Lifetime {
        fn to_tokens(&self, tokens: &mut Tokens) {
            self.ident.to_tokens(tokens);
        }
    }

    impl ToTokens for LifetimeDef {
        fn to_tokens(&self, tokens: &mut Tokens) {
            self.lifetime.to_tokens(tokens);
            if !self.bounds.is_empty() {
                tokens.append(":");
                tokens.append_separated(&self.bounds, "+");
            }
        }
    }

    impl ToTokens for TyParamBound {
        fn to_tokens(&self, tokens: &mut Tokens) {
            match *self {
                TyParamBound::MaybeSized => tokens.append("?Sized"),
                TyParamBound::Region(ref lifetime) => lifetime.to_tokens(tokens),
                TyParamBound::Trait(ref trait_ref) => trait_ref.to_tokens(tokens),
            }
        }
    }
}
<commit_msg>Printing for generics<commit_after>use super::*;

#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct Generics {
    pub lifetimes: Vec<LifetimeDef>,
    pub ty_params: Vec<TyParam>,
    pub where_clause: Vec<WherePredicate>,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Lifetime {
    pub ident: Ident,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct LifetimeDef {
    pub lifetime: Lifetime,
    pub bounds: Vec<Lifetime>,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub struct TyParam {
    pub ident: Ident,
    pub bounds: Vec<TyParamBound>,
    pub default: Option<Ty>,
}

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum TyParamBound {
    MaybeSized,
    Region(Lifetime),
    Trait(PolyTraitRef),
}

/// A single predicate in a `where` clause
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum WherePredicate {
    /// A type binding, e.g. `for<'c> Foo: Send+Clone+'c`
    BoundPredicate(WhereBoundPredicate),
    /// A lifetime predicate, e.g. `'a: 'b+'c`
    RegionPredicate(WhereRegionPredicate),
}

/// A type bound.
///
/// E.g. `for<'c> Foo: Send+Clone+'c`
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct WhereBoundPredicate {
    /// Any lifetimes from a `for` binding
    pub bound_lifetimes: Vec<LifetimeDef>,
    /// The type being bounded
    pub bounded_ty: Ty,
    /// Trait and lifetime bounds (`Clone+Send+'static`)
    pub bounds: Vec<TyParamBound>,
}

/// A lifetime predicate.
///
/// E.g. `'a: 'b+'c`
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct WhereRegionPredicate {
    pub lifetime: Lifetime,
    pub bounds: Vec<Lifetime>,
}

#[cfg(feature = "parsing")]
pub mod parsing {
    use super::*;
    use common::parsing::word;
    use ty::parsing::{ty, poly_trait_ref};
    use nom::multispace;

    named!(pub generics<&str, Generics>, do_parse!(
        bracketed: alt!(
            do_parse!(
                punct!("<") >>
                lifetimes: separated_list!(punct!(","), lifetime_def) >>
                ty_params: opt_vec!(preceded!(
                    cond!(!lifetimes.is_empty(), punct!(",")),
                    separated_nonempty_list!(punct!(","), ty_param)
                )) >>
                punct!(">") >>
                (lifetimes, ty_params)
            )
            |
            epsilon!() => { |_| (Vec::new(), Vec::new()) }
        ) >>
        where_clause: opt_vec!(do_parse!(
            punct!("where") >>
            multispace >>
            predicates: separated_nonempty_list!(punct!(","), where_predicate) >>
            opt!(punct!(",")) >>
            (predicates)
        )) >>
        (Generics {
            lifetimes: bracketed.0,
            ty_params: bracketed.1,
            where_clause: where_clause,
        })
    ));

    named!(pub lifetime<&str, Lifetime>, preceded!(
        punct!("'"),
        map!(word, |ident| Lifetime {
            ident: format!("'{}", ident).into(),
        })
    ));

    named!(pub lifetime_def<&str, LifetimeDef>, do_parse!(
        life: lifetime >>
        bounds: opt_vec!(preceded!(
            punct!(":"),
            separated_nonempty_list!(punct!(","), lifetime)
        )) >>
        (LifetimeDef {
            lifetime: life,
            bounds: bounds,
        })
    ));

    named!(pub bound_lifetimes<&str, Vec<LifetimeDef> >, opt_vec!(do_parse!(
        punct!("for") >>
        punct!("<") >>
        lifetimes: separated_list!(punct!(","), lifetime_def) >>
        punct!(">") >>
        (lifetimes)
    )));

    named!(ty_param<&str, TyParam>, do_parse!(
        ident: word >>
        bounds: opt_vec!(preceded!(
            punct!(":"),
            separated_nonempty_list!(punct!("+"), ty_param_bound)
        )) >>
        default: opt!(preceded!(
            punct!("="),
            ty
        )) >>
        (TyParam {
            ident: ident,
            bounds: bounds,
            default: default,
        })
    ));

    named!(pub ty_param_bound<&str, TyParamBound>, alt!(
        tuple!(punct!("?"), punct!("Sized")) => { |_| TyParamBound::MaybeSized }
        |
        lifetime => { TyParamBound::Region }
        |
        poly_trait_ref => { TyParamBound::Trait }
    ));

    named!(where_predicate<&str, WherePredicate>, alt!(
        do_parse!(
            ident: lifetime >>
            punct!(":") >>
            bounds: separated_nonempty_list!(punct!("+"), lifetime) >>
            (WherePredicate::RegionPredicate(WhereRegionPredicate {
                lifetime: ident,
                bounds: bounds,
            }))
        )
        |
        do_parse!(
            bound_lifetimes: bound_lifetimes >>
            bounded_ty: ty >>
            punct!(":") >>
            bounds: separated_nonempty_list!(punct!("+"), ty_param_bound) >>
            (WherePredicate::BoundPredicate(WhereBoundPredicate {
                bound_lifetimes: bound_lifetimes,
                bounded_ty: bounded_ty,
                bounds: bounds,
            }))
        )
    ));
}

#[cfg(feature = "printing")]
mod printing {
    use super::*;
    use quote::{Tokens, ToTokens};

    impl ToTokens for Generics {
        fn to_tokens(&self, tokens: &mut Tokens) {
            let has_lifetimes = !self.lifetimes.is_empty();
            let has_ty_params = !self.ty_params.is_empty();
            if has_lifetimes || has_ty_params {
                tokens.append("<");
                tokens.append_separated(&self.lifetimes, ",");
                if has_lifetimes && has_ty_params {
                    tokens.append(",");
                }
                tokens.append_separated(&self.ty_params, ",");
                tokens.append(">");
            }
            if !self.where_clause.is_empty() {
                tokens.append("where");
                tokens.append_separated(&self.where_clause, ",");
            }
        }
    }

    impl ToTokens for Lifetime {
        fn to_tokens(&self, tokens: &mut Tokens) {
            self.ident.to_tokens(tokens);
        }
    }

    impl ToTokens for LifetimeDef {
        fn to_tokens(&self, tokens: &mut Tokens) {
            self.lifetime.to_tokens(tokens);
            if !self.bounds.is_empty() {
                tokens.append(":");
                tokens.append_separated(&self.bounds, "+");
            }
        }
    }

    impl ToTokens for TyParam {
        fn to_tokens(&self, tokens: &mut Tokens) {
            self.ident.to_tokens(tokens);
            if !self.bounds.is_empty() {
                tokens.append(":");
                tokens.append_separated(&self.bounds, ",");
            }
            if let Some(ref default) = self.default {
                tokens.append("=");
                default.to_tokens(tokens);
            }
        }
    }

    impl ToTokens for TyParamBound {
        fn to_tokens(&self, tokens: &mut Tokens) {
            match *self {
                TyParamBound::MaybeSized => tokens.append("?Sized"),
                TyParamBound::Region(ref lifetime) => lifetime.to_tokens(tokens),
                TyParamBound::Trait(ref trait_ref) => trait_ref.to_tokens(tokens),
            }
        }
    }

    impl ToTokens for WherePredicate {
        fn to_tokens(&self, tokens: &mut Tokens) {
            match *self {
                WherePredicate::BoundPredicate(ref predicate) => {
                    predicate.to_tokens(tokens);
                }
                WherePredicate::RegionPredicate(ref predicate) => {
                    predicate.to_tokens(tokens);
                }
            }
        }
    }

    impl ToTokens for WhereBoundPredicate {
        fn to_tokens(&self, tokens: &mut Tokens) {
            if !self.bound_lifetimes.is_empty() {
                tokens.append("for");
                tokens.append("<");
                tokens.append_separated(&self.bound_lifetimes, ",");
                tokens.append(">");
            }
            self.bounded_ty.to_tokens(tokens);
            if !self.bounds.is_empty() {
                tokens.append(":");
                tokens.append_separated(&self.bounds, "+");
            }
        }
    }

    impl ToTokens for WhereRegionPredicate {
        fn to_tokens(&self, tokens: &mut Tokens) {
            self.lifetime.to_tokens(tokens);
            if !self.bounds.is_empty() {
                tokens.append(":");
                tokens.append_separated(&self.bounds, "+");
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Trait solving using Chalk.
use std::{
    panic,
    sync::{Arc, Mutex},
};

use chalk_ir::cast::Cast;
use hir_def::{expr::ExprId, DefWithBodyId, ImplId, TraitId, TypeAliasId};
use ra_db::{impl_intern_key, salsa, Canceled, CrateId};
use ra_prof::profile;
use rustc_hash::FxHashSet;

use crate::db::HirDatabase;

use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk};

use self::chalk::{from_chalk, ToChalk, TypeFamily};

pub(crate) mod chalk;
mod builtin;

#[derive(Debug, Clone)]
pub struct TraitSolver {
    krate: CrateId,
    inner: Arc<Mutex<chalk_solve::Solver<TypeFamily>>>,
}

/// We need eq for salsa
impl PartialEq for TraitSolver {
    fn eq(&self, other: &TraitSolver) -> bool {
        Arc::ptr_eq(&self.inner, &other.inner)
    }
}

impl Eq for TraitSolver {}

impl TraitSolver {
    fn solve(
        &self,
        db: &impl HirDatabase,
        goal: &chalk_ir::UCanonical<chalk_ir::InEnvironment<chalk_ir::Goal<TypeFamily>>>,
    ) -> Option<chalk_solve::Solution<TypeFamily>> {
        let context = ChalkContext { db, krate: self.krate };
        log::debug!("solve goal: {:?}", goal);
        let mut solver = match self.inner.lock() {
            Ok(it) => it,
            // Our cancellation works via unwinding, but, as chalk is not
            // panic-safe, we need to make sure to propagate the cancellation.
            // Ideally, we should also make chalk panic-safe.
            Err(_) => ra_db::Canceled::throw(),
        };

        let fuel = std::cell::Cell::new(CHALK_SOLVER_FUEL);

        let solution = panic::catch_unwind({
            let solver = panic::AssertUnwindSafe(&mut solver);
            let context = panic::AssertUnwindSafe(&context);
            move || {
                solver.0.solve_limited(context.0, goal, || {
                    context.0.db.check_canceled();
                    let remaining = fuel.get();
                    fuel.set(remaining - 1);
                    remaining > 0
                })
            }
        });

        let solution = match solution {
            Ok(it) => it,
            Err(err) => {
                if err.downcast_ref::<Canceled>().is_some() {
                    panic::resume_unwind(err)
                } else {
                    log::error!("chalk panicked :-(");
                    // Reset the solver, as it is not panic-safe.
                    *solver = create_chalk_solver();
                    None
                }
            }
        };

        log::debug!("solve({:?}) => {:?}", goal, solution);
        solution
    }
}

/// This controls the maximum size of types Chalk considers. If we set this too
/// high, we can run into slow edge cases; if we set it too low, Chalk won't
/// find some solutions.
const CHALK_SOLVER_MAX_SIZE: usize = 4;
/// This controls how much 'time' we give the Chalk solver before giving up.
const CHALK_SOLVER_FUEL: i32 = 100;
// TODO: tune both these values

#[derive(Debug, Copy, Clone)]
struct ChalkContext<'a, DB> {
    db: &'a DB,
    krate: CrateId,
}

pub(crate) fn trait_solver_query(
    db: &(impl HirDatabase + salsa::Database),
    krate: CrateId,
) -> TraitSolver {
    db.salsa_runtime().report_untracked_read();
    // krate parameter is just so we cache a unique solver per crate
    log::debug!("Creating new solver for crate {:?}", krate);
    TraitSolver { krate, inner: Arc::new(Mutex::new(create_chalk_solver())) }
}

fn create_chalk_solver() -> chalk_solve::Solver<TypeFamily> {
    let solver_choice =
        chalk_solve::SolverChoice::SLG { max_size: CHALK_SOLVER_MAX_SIZE, expected_answers: None };
    solver_choice.into_solver()
}

/// Collects impls for the given trait in the whole dependency tree of `krate`.
pub(crate) fn impls_for_trait_query(
    db: &impl HirDatabase,
    krate: CrateId,
    trait_: TraitId,
) -> Arc<[ImplId]> {
    let mut impls = FxHashSet::default();
    // We call the query recursively here. On the one hand, this means we can
    // reuse results from queries for different crates; on the other hand, this
    // will only ever get called for a few crates near the root of the tree (the
    // ones the user is editing), so this may actually be a waste of memory. I'm
    // doing it like this mainly for simplicity for now.
    for dep in db.crate_graph().dependencies(krate) {
        impls.extend(db.impls_for_trait(dep.crate_id, trait_).iter());
    }
    let crate_impl_blocks = db.impls_in_crate(krate);
    impls.extend(crate_impl_blocks.lookup_impl_blocks_for_trait(trait_));
    impls.into_iter().collect()
}

/// A set of clauses that we assume to be true. E.g. if we are inside this function:
/// ```rust
/// fn foo<T: Default>(t: T) {}
/// ```
/// we assume that `T: Default`.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct TraitEnvironment {
    pub predicates: Vec<GenericPredicate>,
}

impl TraitEnvironment {
    /// Returns trait refs with the given self type which are supposed to hold
    /// in this trait env. E.g. if we are in `foo<T: SomeTrait>()`, this will
    /// find that `T: SomeTrait` if we call it for `T`.
    pub(crate) fn trait_predicates_for_self_ty<'a>(
        &'a self,
        ty: &'a Ty,
    ) -> impl Iterator<Item = &'a TraitRef> + 'a {
        self.predicates.iter().filter_map(move |pred| match pred {
            GenericPredicate::Implemented(tr) if tr.self_ty() == ty => Some(tr),
            _ => None,
        })
    }
}

/// Something (usually a goal), along with an environment.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct InEnvironment<T> {
    pub environment: Arc<TraitEnvironment>,
    pub value: T,
}

impl<T> InEnvironment<T> {
    pub fn new(environment: Arc<TraitEnvironment>, value: T) -> InEnvironment<T> {
        InEnvironment { environment, value }
    }
}

/// Something that needs to be proven (by Chalk) during type checking, e.g. that
/// a certain type implements a certain trait. Proving the Obligation might
/// result in additional information about inference variables.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Obligation {
    /// Prove that a certain type implements a trait (the type is the `Self` type
    /// parameter to the `TraitRef`).
    Trait(TraitRef),
    Projection(ProjectionPredicate),
}

impl Obligation {
    pub fn from_predicate(predicate: GenericPredicate) -> Option<Obligation> {
        match predicate {
            GenericPredicate::Implemented(trait_ref) => Some(Obligation::Trait(trait_ref)),
            GenericPredicate::Projection(projection_pred) => {
                Some(Obligation::Projection(projection_pred))
            }
            GenericPredicate::Error => None,
        }
    }
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ProjectionPredicate {
    pub projection_ty: ProjectionTy,
    pub ty: Ty,
}

impl TypeWalk for ProjectionPredicate {
    fn walk(&self, f: &mut impl FnMut(&Ty)) {
        self.projection_ty.walk(f);
        self.ty.walk(f);
    }

    fn walk_mut_binders(&mut self, f: &mut impl FnMut(&mut Ty, usize), binders: usize) {
        self.projection_ty.walk_mut_binders(f, binders);
        self.ty.walk_mut_binders(f, binders);
    }
}

/// Solve a trait goal using Chalk.
pub(crate) fn trait_solve_query(
    db: &impl HirDatabase,
    krate: CrateId,
    goal: Canonical<InEnvironment<Obligation>>,
) -> Option<Solution> {
    let _p = profile("trait_solve_query");
    log::debug!("trait_solve_query({})", goal.value.value.display(db));

    if let Obligation::Projection(pred) = &goal.value.value {
        if let Ty::Bound(_) = &pred.projection_ty.parameters[0] {
            // Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible
            return Some(Solution::Ambig(Guidance::Unknown));
        }
    }

    let canonical = goal.to_chalk(db).cast();

    // We currently don't deal with universes (I think / hope they're not yet
    // relevant for our use cases?)
    let u_canonical = chalk_ir::UCanonical { canonical, universes: 1 };
    let solution = db.trait_solver(krate).solve(db, &u_canonical);
    solution.map(|solution| solution_from_chalk(db, solution))
}

fn solution_from_chalk(
    db: &impl HirDatabase,
    solution: chalk_solve::Solution<TypeFamily>,
) -> Solution {
    let convert_subst = |subst: chalk_ir::Canonical<chalk_ir::Substitution<TypeFamily>>| {
        let value = subst
            .value
            .into_iter()
            .map(|p| {
                let ty = match p.ty() {
                    Some(ty) => from_chalk(db, ty.clone()),
                    None => unimplemented!(),
                };
                ty
            })
            .collect();
        let result = Canonical { value, num_vars: subst.binders.len() };
        SolutionVariables(result)
    };
    match solution {
        chalk_solve::Solution::Unique(constr_subst) => {
            let subst = chalk_ir::Canonical {
                value: constr_subst.value.subst,
                binders: constr_subst.binders,
            };
            Solution::Unique(convert_subst(subst))
        }
        chalk_solve::Solution::Ambig(chalk_solve::Guidance::Definite(subst)) => {
            Solution::Ambig(Guidance::Definite(convert_subst(subst)))
        }
        chalk_solve::Solution::Ambig(chalk_solve::Guidance::Suggested(subst)) => {
            Solution::Ambig(Guidance::Suggested(convert_subst(subst)))
        }
        chalk_solve::Solution::Ambig(chalk_solve::Guidance::Unknown) => {
            Solution::Ambig(Guidance::Unknown)
        }
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SolutionVariables(pub Canonical<Vec<Ty>>);

#[derive(Clone, Debug, PartialEq, Eq)]
/// A (possible) solution for a proposed goal.
pub enum Solution {
    /// The goal indeed holds, and there is a unique value for all existential
    /// variables.
    Unique(SolutionVariables),

    /// The goal may be provable in multiple ways, but regardless we may have some guidance
    /// for type inference. In this case, we don't return any lifetime
    /// constraints, since we have not "committed" to any particular solution
    /// yet.
    Ambig(Guidance),
}

#[derive(Clone, Debug, PartialEq, Eq)]
/// When a goal holds ambiguously (e.g., because there are multiple possible
/// solutions), we issue a set of *guidance* back to type inference.
pub enum Guidance {
    /// The existential variables *must* have the given values if the goal is
    /// ever to hold, but that alone isn't enough to guarantee the goal will
    /// actually hold.
    Definite(SolutionVariables),

    /// There are multiple plausible values for the existentials, but the ones
    /// here are suggested as the preferred choice heuristically. These should
    /// be used for inference fallback only.
    Suggested(SolutionVariables),

    /// There's no useful information to feed back to type inference
    Unknown,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum FnTrait {
    FnOnce,
    FnMut,
    Fn,
}

impl FnTrait {
    fn lang_item_name(self) -> &'static str {
        match self {
            FnTrait::FnOnce => "fn_once",
            FnTrait::FnMut => "fn_mut",
            FnTrait::Fn => "fn",
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ClosureFnTraitImplData {
    def: DefWithBodyId,
    expr: ExprId,
    fn_trait: FnTrait,
}

/// An impl. Usually this comes from an impl block, but some built-in types get
/// synthetic impls.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Impl {
    /// A normal impl from an impl block.
    ImplBlock(ImplId),
    /// Closure types implement the Fn traits synthetically.
    ClosureFnTraitImpl(ClosureFnTraitImplData),
}
/// This exists just for Chalk, because our ImplIds are only unique per module.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct GlobalImplId(salsa::InternId);
impl_intern_key!(GlobalImplId);

/// An associated type value. Usually this comes from a `type` declaration
/// inside an impl block, but for built-in impls we have to synthesize it.
/// (We only need this because Chalk wants a unique ID for each of these.)
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum AssocTyValue {
    /// A normal assoc type value from an impl block.
    TypeAlias(TypeAliasId),
    /// The output type of the Fn trait implementation.
    ClosureFnTraitImplOutput(ClosureFnTraitImplData),
}
/// This exists just for Chalk, because it needs a unique ID for each associated
/// type value in an impl (even synthetic ones).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct AssocTyValueId(salsa::InternId);
impl_intern_key!(AssocTyValueId);
<commit_msg>Tweak Chalk settings<commit_after>//! Trait solving using Chalk.
use std::{
    panic,
    sync::{Arc, Mutex},
};

use chalk_ir::cast::Cast;
use hir_def::{expr::ExprId, DefWithBodyId, ImplId, TraitId, TypeAliasId};
use ra_db::{impl_intern_key, salsa, Canceled, CrateId};
use ra_prof::profile;
use rustc_hash::FxHashSet;

use crate::db::HirDatabase;

use super::{Canonical, GenericPredicate, HirDisplay, ProjectionTy, TraitRef, Ty, TypeWalk};

use self::chalk::{from_chalk, ToChalk, TypeFamily};

pub(crate) mod chalk;
mod builtin;

#[derive(Debug, Clone)]
pub struct TraitSolver {
    krate: CrateId,
    inner: Arc<Mutex<chalk_solve::Solver<TypeFamily>>>,
}

/// We need eq for salsa
impl PartialEq for TraitSolver {
    fn eq(&self, other: &TraitSolver) -> bool {
        Arc::ptr_eq(&self.inner, &other.inner)
    }
}

impl Eq for TraitSolver {}

impl TraitSolver {
    fn solve(
        &self,
        db: &impl HirDatabase,
        goal: &chalk_ir::UCanonical<chalk_ir::InEnvironment<chalk_ir::Goal<TypeFamily>>>,
    ) -> Option<chalk_solve::Solution<TypeFamily>> {
        let context = ChalkContext { db, krate: self.krate };
        log::debug!("solve goal: {:?}", goal);
        let mut solver = match self.inner.lock() {
            Ok(it) => it,
            // Our cancellation works via unwinding, but, as chalk is not
            // panic-safe, we need to make sure to propagate the cancellation.
            // Ideally, we should also make chalk panic-safe.
            Err(_) => ra_db::Canceled::throw(),
        };

        let fuel = std::cell::Cell::new(CHALK_SOLVER_FUEL);

        let solution = panic::catch_unwind({
            let solver = panic::AssertUnwindSafe(&mut solver);
            let context = panic::AssertUnwindSafe(&context);
            move || {
                solver.0.solve_limited(context.0, goal, || {
                    context.0.db.check_canceled();
                    let remaining = fuel.get();
                    fuel.set(remaining - 1);
                    remaining > 0
                })
            }
        });

        let solution = match solution {
            Ok(it) => it,
            Err(err) => {
                if err.downcast_ref::<Canceled>().is_some() {
                    panic::resume_unwind(err)
                } else {
                    log::error!("chalk panicked :-(");
                    // Reset the solver, as it is not panic-safe.
                    *solver = create_chalk_solver();
                    None
                }
            }
        };

        log::debug!("solve({:?}) => {:?}", goal, solution);
        solution
    }
}

/// This controls the maximum size of types Chalk considers. If we set this too
/// high, we can run into slow edge cases; if we set it too low, Chalk won't
/// find some solutions.
const CHALK_SOLVER_MAX_SIZE: usize = 10;
/// This controls how much 'time' we give the Chalk solver before giving up.
const CHALK_SOLVER_FUEL: i32 = 100;

#[derive(Debug, Copy, Clone)]
struct ChalkContext<'a, DB> {
    db: &'a DB,
    krate: CrateId,
}

pub(crate) fn trait_solver_query(
    db: &(impl HirDatabase + salsa::Database),
    krate: CrateId,
) -> TraitSolver {
    db.salsa_runtime().report_untracked_read();
    // krate parameter is just so we cache a unique solver per crate
    log::debug!("Creating new solver for crate {:?}", krate);
    TraitSolver { krate, inner: Arc::new(Mutex::new(create_chalk_solver())) }
}

fn create_chalk_solver() -> chalk_solve::Solver<TypeFamily> {
    let solver_choice =
        chalk_solve::SolverChoice::SLG { max_size: CHALK_SOLVER_MAX_SIZE, expected_answers: None };
    solver_choice.into_solver()
}

/// Collects impls for the given trait in the whole dependency tree of `krate`.
pub(crate) fn impls_for_trait_query(
    db: &impl HirDatabase,
    krate: CrateId,
    trait_: TraitId,
) -> Arc<[ImplId]> {
    let mut impls = FxHashSet::default();
    // We call the query recursively here. On the one hand, this means we can
    // reuse results from queries for different crates; on the other hand, this
    // will only ever get called for a few crates near the root of the tree (the
    // ones the user is editing), so this may actually be a waste of memory. I'm
    // doing it like this mainly for simplicity for now.
    for dep in db.crate_graph().dependencies(krate) {
        impls.extend(db.impls_for_trait(dep.crate_id, trait_).iter());
    }
    let crate_impl_blocks = db.impls_in_crate(krate);
    impls.extend(crate_impl_blocks.lookup_impl_blocks_for_trait(trait_));
    impls.into_iter().collect()
}

/// A set of clauses that we assume to be true. E.g. if we are inside this function:
/// ```rust
/// fn foo<T: Default>(t: T) {}
/// ```
/// we assume that `T: Default`.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct TraitEnvironment {
    pub predicates: Vec<GenericPredicate>,
}

impl TraitEnvironment {
    /// Returns trait refs with the given self type which are supposed to hold
    /// in this trait env. E.g. if we are in `foo<T: SomeTrait>()`, this will
    /// find that `T: SomeTrait` if we call it for `T`.
    pub(crate) fn trait_predicates_for_self_ty<'a>(
        &'a self,
        ty: &'a Ty,
    ) -> impl Iterator<Item = &'a TraitRef> + 'a {
        self.predicates.iter().filter_map(move |pred| match pred {
            GenericPredicate::Implemented(tr) if tr.self_ty() == ty => Some(tr),
            _ => None,
        })
    }
}

/// Something (usually a goal), along with an environment.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct InEnvironment<T> {
    pub environment: Arc<TraitEnvironment>,
    pub value: T,
}

impl<T> InEnvironment<T> {
    pub fn new(environment: Arc<TraitEnvironment>, value: T) -> InEnvironment<T> {
        InEnvironment { environment, value }
    }
}

/// Something that needs to be proven (by Chalk) during type checking, e.g. that
/// a certain type implements a certain trait. Proving the Obligation might
/// result in additional information about inference variables.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum Obligation {
    /// Prove that a certain type implements a trait (the type is the `Self` type
    /// parameter to the `TraitRef`).
    Trait(TraitRef),
    Projection(ProjectionPredicate),
}

impl Obligation {
    pub fn from_predicate(predicate: GenericPredicate) -> Option<Obligation> {
        match predicate {
            GenericPredicate::Implemented(trait_ref) => Some(Obligation::Trait(trait_ref)),
            GenericPredicate::Projection(projection_pred) => {
                Some(Obligation::Projection(projection_pred))
            }
            GenericPredicate::Error => None,
        }
    }
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ProjectionPredicate {
    pub projection_ty: ProjectionTy,
    pub ty: Ty,
}

impl TypeWalk for ProjectionPredicate {
    fn walk(&self, f: &mut impl FnMut(&Ty)) {
        self.projection_ty.walk(f);
        self.ty.walk(f);
    }

    fn walk_mut_binders(&mut self, f: &mut impl FnMut(&mut Ty, usize), binders: usize) {
        self.projection_ty.walk_mut_binders(f, binders);
        self.ty.walk_mut_binders(f, binders);
    }
}

/// Solve a trait goal using Chalk.
pub(crate) fn trait_solve_query(
    db: &impl HirDatabase,
    krate: CrateId,
    goal: Canonical<InEnvironment<Obligation>>,
) -> Option<Solution> {
    let _p = profile("trait_solve_query");
    log::debug!("trait_solve_query({})", goal.value.value.display(db));

    if let Obligation::Projection(pred) = &goal.value.value {
        if let Ty::Bound(_) = &pred.projection_ty.parameters[0] {
            // Hack: don't ask Chalk to normalize with an unknown self type, it'll say that's impossible
            return Some(Solution::Ambig(Guidance::Unknown));
        }
    }

    let canonical = goal.to_chalk(db).cast();

    // We currently don't deal with universes (I think / hope they're not yet
    // relevant for our use cases?)
    let u_canonical = chalk_ir::UCanonical { canonical, universes: 1 };
    let solution = db.trait_solver(krate).solve(db, &u_canonical);
    solution.map(|solution| solution_from_chalk(db, solution))
}

fn solution_from_chalk(
    db: &impl HirDatabase,
    solution: chalk_solve::Solution<TypeFamily>,
) -> Solution {
    let convert_subst = |subst: chalk_ir::Canonical<chalk_ir::Substitution<TypeFamily>>| {
        let value = subst
            .value
            .into_iter()
            .map(|p| {
                let ty = match p.ty() {
                    Some(ty) => from_chalk(db, ty.clone()),
                    None => unimplemented!(),
                };
                ty
            })
            .collect();
        let result = Canonical { value, num_vars: subst.binders.len() };
        SolutionVariables(result)
    };
    match solution {
        chalk_solve::Solution::Unique(constr_subst) => {
            let subst = chalk_ir::Canonical {
                value: constr_subst.value.subst,
                binders: constr_subst.binders,
            };
            Solution::Unique(convert_subst(subst))
        }
        chalk_solve::Solution::Ambig(chalk_solve::Guidance::Definite(subst)) => {
            Solution::Ambig(Guidance::Definite(convert_subst(subst)))
        }
        chalk_solve::Solution::Ambig(chalk_solve::Guidance::Suggested(subst)) => {
            Solution::Ambig(Guidance::Suggested(convert_subst(subst)))
        }
        chalk_solve::Solution::Ambig(chalk_solve::Guidance::Unknown) => {
            Solution::Ambig(Guidance::Unknown)
        }
    }
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SolutionVariables(pub Canonical<Vec<Ty>>);

#[derive(Clone, Debug, PartialEq, Eq)]
/// A (possible) solution for a proposed goal.
pub enum Solution {
    /// The goal indeed holds, and there is a unique value for all existential
    /// variables.
    Unique(SolutionVariables),

    /// The goal may be provable in multiple ways, but regardless we may have some guidance
    /// for type inference. In this case, we don't return any lifetime
    /// constraints, since we have not "committed" to any particular solution
    /// yet.
    Ambig(Guidance),
}

#[derive(Clone, Debug, PartialEq, Eq)]
/// When a goal holds ambiguously (e.g., because there are multiple possible
/// solutions), we issue a set of *guidance* back to type inference.
pub enum Guidance {
    /// The existential variables *must* have the given values if the goal is
    /// ever to hold, but that alone isn't enough to guarantee the goal will
    /// actually hold.
    Definite(SolutionVariables),

    /// There are multiple plausible values for the existentials, but the ones
    /// here are suggested as the preferred choice heuristically. These should
    /// be used for inference fallback only.
    Suggested(SolutionVariables),

    /// There's no useful information to feed back to type inference
    Unknown,
}

#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum FnTrait {
    FnOnce,
    FnMut,
    Fn,
}

impl FnTrait {
    fn lang_item_name(self) -> &'static str {
        match self {
            FnTrait::FnOnce => "fn_once",
            FnTrait::FnMut => "fn_mut",
            FnTrait::Fn => "fn",
        }
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct ClosureFnTraitImplData {
    def: DefWithBodyId,
    expr: ExprId,
    fn_trait: FnTrait,
}

/// An impl. Usually this comes from an impl block, but some built-in types get
/// synthetic impls.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Impl {
    /// A normal impl from an impl block.
    ImplBlock(ImplId),
    /// Closure types implement the Fn traits synthetically.
    ClosureFnTraitImpl(ClosureFnTraitImplData),
}
/// This exists just for Chalk, because our ImplIds are only unique per module.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct GlobalImplId(salsa::InternId);
impl_intern_key!(GlobalImplId);

/// An associated type value. Usually this comes from a `type` declaration
/// inside an impl block, but for built-in impls we have to synthesize it.
/// (We only need this because Chalk wants a unique ID for each of these.)
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum AssocTyValue {
    /// A normal assoc type value from an impl block.
    TypeAlias(TypeAliasId),
    /// The output type of the Fn trait implementation.
    ClosureFnTraitImplOutput(ClosureFnTraitImplData),
}
/// This exists just for Chalk, because it needs a unique ID for each associated
/// type value in an impl (even synthetic ones).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct AssocTyValueId(salsa::InternId);
impl_intern_key!(AssocTyValueId);
<|endoftext|>