<commit_before>/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use clap::{ArgEnum, Parser};
use common::ConsoleLogger;
use log::{error, info};
use relay_compiler::{
    build_project::artifact_writer::ArtifactValidationWriter, compiler::Compiler, config::Config,
    FileSourceKind, LocalPersister, OperationPersister, PersistConfig, RemotePersister,
};
use relay_lsp::{start_language_server, DummyExtraDataProvider};
use schema::SDLSchema;
use schema_documentation::SchemaDocumentationLoader;
use simplelog::{
    ColorChoice, ConfigBuilder as SimpleLogConfigBuilder, LevelFilter, TermLogger, TerminalMode,
};
use std::{
    env::{self, current_dir},
    path::PathBuf,
    process::Command,
    sync::Arc,
};

mod errors;

use errors::Error;

#[derive(Parser)]
#[clap(
     name = "Relay Compiler",
     version = option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"),
     about = "Compiles Relay files and writes generated files.",
     rename_all = "camel_case",
     args_conflicts_with_subcommands = true
 )]
struct Opt {
    #[clap(subcommand)]
    command: Option<Commands>,

    #[clap(flatten)]
    compile: CompileCommand,
}

#[derive(Parser)]
#[clap(
    rename_all = "camel_case",
    about = "Compiles Relay files and writes generated files."
)]
struct CompileCommand {
    /// Compile and watch for changes
    #[clap(long, short)]
    watch: bool,

    /// Compile using this config file. If not provided, searches for a config in
    /// package.json under the `relay` key or `relay.config.json` files among other up
    /// from the current working directory.
    config: Option<PathBuf>,

    #[clap(flatten)]
    cli_config: CliConfig,

    /// Run the persister even if the query has not changed.
    #[clap(long)]
    repersist: bool,

    /// Verbosity level
    #[clap(long, arg_enum, default_value = "verbose")]
    output: OutputKind,

    /// Looks for pending changes and exits with non-zero code instead of
    /// writing to disk
    #[clap(long)]
    validate: bool,
}

#[derive(Parser)]
#[clap(
    about = "Run the language server. Used by IDEs.",
    rename_all = "camel_case"
)]
struct LspCommand {
    /// Run the LSP using this config file. If not provided, searches for a config in
    /// package.json under the `relay` key or `relay.config.json` files among other up
    /// from the current working directory.
    config: Option<PathBuf>,

    /// Verbosity level
    #[clap(long, arg_enum, default_value = "info")]
    output: OutputKind,
}

#[derive(clap::Subcommand)]
enum Commands {
    Compiler(CompileCommand),
    Lsp(LspCommand),
}

#[derive(ArgEnum, Clone, Copy)]
enum OutputKind {
    Debug,
    Quiet,
    QuietWithErrors,
    Verbose,
}

#[derive(Parser)]
#[clap(rename_all = "camel_case")]
pub struct CliConfig {
    /// Path for the directory where to search for source code
    #[clap(long)]
    pub src: Option<String>,
    /// Path to schema file
    #[clap(long)]
    pub schema: Option<String>,
    /// Path to a directory, where the compiler should write artifacts
    #[clap(long)]
    pub artifact_directory: Option<String>,
}

impl CliConfig {
    fn is_defined(&self) -> bool {
        self.src.is_some() || self.schema.is_some() || self.artifact_directory.is_some()
    }

    fn get_config_string(self) -> String {
        let src = self.src.unwrap_or_else(|| "./src".into());
        let schema = self.schema.unwrap_or_else(|| "./path-to-schema".into());
        let artifact_directory = self.artifact_directory.map_or("".to_string(), |a| {
            format!("\n  \"artifactDirectory\": \"{}\",", a)
        });
        format!(
            r#"
 {{
   "src": "{}",
   "schema": "{}",{}
   "language": "javascript"
 }}"#,
            src, schema, artifact_directory
        )
    }
}

#[tokio::main]
async fn main() {
    let opt = Opt::parse();

    let command = opt.command.unwrap_or(Commands::Compiler(opt.compile));

    let result = match command {
        Commands::Compiler(command) => handle_compiler_command(command).await,
        Commands::Lsp(command) => handle_lsp_command(command).await,
    };

    match result {
        Ok(_) => info!("Done."),
        Err(err) => {
            error!("{:?}", err);
            std::process::exit(1);
        }
    }
}

fn get_config(config_path: Option<PathBuf>) -> Result<Config, Error> {
    match config_path {
        Some(config_path) => Config::load(config_path).map_err(|err| Error::ConfigError {
            details: format!("{:?}", err),
        }),
        None => Config::search(&current_dir().expect("Unable to get current working directory."))
            .map_err(|err| Error::ConfigError {
                details: format!("{:?}", err),
            }),
    }
}

fn configure_logger(output: OutputKind, terminal_mode: TerminalMode) {
    let log_level = match output {
        OutputKind::Debug => LevelFilter::Debug,
        OutputKind::Quiet => LevelFilter::Off,
        OutputKind::QuietWithErrors => LevelFilter::Error,
        OutputKind::Verbose => LevelFilter::Info,
    };

    let log_config = SimpleLogConfigBuilder::new()
        .set_time_level(LevelFilter::Off)
        .set_target_level(LevelFilter::Off)
        .set_location_level(LevelFilter::Off)
        .set_thread_level(LevelFilter::Off)
        .build();

    TermLogger::init(log_level, log_config, terminal_mode, ColorChoice::Auto).unwrap();
}

async fn handle_compiler_command(command: CompileCommand) -> Result<(), Error> {
    configure_logger(command.output, TerminalMode::Mixed);

    if command.cli_config.is_defined() {
        return Err(Error::ConfigError {
            details: format!(
                "\nPassing Relay compiler configuration is not supported. Please add `relay.config.json` file,\nor \"relay\" section to your `package.json` file.\n\nCompiler configuration JSON:{}",
                command.cli_config.get_config_string(),
            ),
        });
    }

    let mut config = get_config(command.config)?;

    if command.validate {
        config.artifact_writer = Box::new(ArtifactValidationWriter::default());
    }

    config.create_operation_persister = Some(Box::new(|project_config| {
        project_config.persist.as_ref().map(
            |persist_config| -> Box<dyn OperationPersister + Send + Sync> {
                match persist_config {
                    PersistConfig::Remote(remote_config) => {
                        Box::new(RemotePersister::new(remote_config.clone()))
                    }
                    PersistConfig::Local(local_config) => {
                        Box::new(LocalPersister::new(local_config.clone()))
                    }
                }
            },
        )
    }));

    config.file_source_config = if should_use_watchman() {
        FileSourceKind::Watchman
    } else {
        FileSourceKind::WalkDir
    };
    config.repersist_operations = command.repersist;

    if command.watch && !matches!(&config.file_source_config, FileSourceKind::Watchman) {
        panic!(
            "Cannot run relay in watch mode if `watchman` is not available (or explicitly disabled)."
        );
    }

    let compiler = Compiler::new(Arc::new(config), Arc::new(ConsoleLogger));

    if command.watch {
        compiler.watch().await.map_err(|err| Error::CompilerError {
            details: format!("{:?}", err),
        })?;
    } else {
        compiler
            .compile()
            .await
            .map_err(|err| Error::CompilerError {
                details: format!("{:?}", err),
            })?;
    }

    Ok(())
}

async fn handle_lsp_command(command: LspCommand) -> Result<(), Error> {
    configure_logger(command.output, TerminalMode::Stderr);

    let config = get_config(command.config)?;

    let perf_logger = Arc::new(ConsoleLogger);
    let extra_data_provider = Box::new(DummyExtraDataProvider::new());
    let schema_documentation_loader: Option<Box<dyn SchemaDocumentationLoader<SDLSchema>>> = None;
    let js_language_server = None;

    start_language_server(
        config,
        perf_logger,
        extra_data_provider,
        schema_documentation_loader,
        js_language_server,
    )
    .await
    .map_err(|err| Error::LSPError {
        details: format!("Relay LSP unexpectedly terminated: {:?}", err),
    })?;

    info!("Relay LSP exited successfully.");

    Ok(())
}

/// Check if `watchman` is available.
/// Additionally, this method is checking for an existence of `FORCE_NO_WATCHMAN`
/// environment variable. If this `FORCE_NO_WATCHMAN` is set, this method will return `false`
/// and compiler will use non-watchman file finder.
fn should_use_watchman() -> bool {
    let check_watchman = Command::new("watchman")
        .args(["list-capabilities"])
        .output();

    check_watchman.is_ok() && env::var("FORCE_NO_WATCHMAN").is_err()
}
<commit_msg>Fix relay LSP crash (#3854)<commit_after>/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use clap::{ArgEnum, Parser};
use common::ConsoleLogger;
use log::{error, info};
use relay_compiler::{
    build_project::artifact_writer::ArtifactValidationWriter, compiler::Compiler, config::Config,
    FileSourceKind, LocalPersister, OperationPersister, PersistConfig, RemotePersister,
};
use relay_lsp::{start_language_server, DummyExtraDataProvider};
use schema::SDLSchema;
use schema_documentation::SchemaDocumentationLoader;
use simplelog::{
    ColorChoice, ConfigBuilder as SimpleLogConfigBuilder, LevelFilter, TermLogger, TerminalMode,
};
use std::{
    env::{self, current_dir},
    path::PathBuf,
    process::Command,
    sync::Arc,
};

mod errors;

use errors::Error;

#[derive(Parser)]
#[clap(
     name = "Relay Compiler",
     version = option_env!("CARGO_PKG_VERSION").unwrap_or("unknown"),
     about = "Compiles Relay files and writes generated files.",
     rename_all = "camel_case",
     args_conflicts_with_subcommands = true
 )]
struct Opt {
    #[clap(subcommand)]
    command: Option<Commands>,

    #[clap(flatten)]
    compile: CompileCommand,
}

#[derive(Parser)]
#[clap(
    rename_all = "camel_case",
    about = "Compiles Relay files and writes generated files."
)]
struct CompileCommand {
    /// Compile and watch for changes
    #[clap(long, short)]
    watch: bool,

    /// Compile using this config file. If not provided, searches for a config in
    /// package.json under the `relay` key or `relay.config.json` files among other up
    /// from the current working directory.
    config: Option<PathBuf>,

    #[clap(flatten)]
    cli_config: CliConfig,

    /// Run the persister even if the query has not changed.
    #[clap(long)]
    repersist: bool,

    /// Verbosity level
    #[clap(long, arg_enum, default_value = "verbose")]
    output: OutputKind,

    /// Looks for pending changes and exits with non-zero code instead of
    /// writing to disk
    #[clap(long)]
    validate: bool,
}

#[derive(Parser)]
#[clap(
    about = "Run the language server. Used by IDEs.",
    rename_all = "camel_case"
)]
struct LspCommand {
    /// Run the LSP using this config file. If not provided, searches for a config in
    /// package.json under the `relay` key or `relay.config.json` files among other up
    /// from the current working directory.
    config: Option<PathBuf>,

    /// Verbosity level
    #[clap(long, arg_enum, default_value = "quiet-with-errors")]
    output: OutputKind,
}

#[derive(clap::Subcommand)]
enum Commands {
    Compiler(CompileCommand),
    Lsp(LspCommand),
}

#[derive(ArgEnum, Clone, Copy)]
enum OutputKind {
    Debug,
    Quiet,
    QuietWithErrors,
    Verbose,
}

#[derive(Parser)]
#[clap(rename_all = "camel_case")]
pub struct CliConfig {
    /// Path for the directory where to search for source code
    #[clap(long)]
    pub src: Option<String>,
    /// Path to schema file
    #[clap(long)]
    pub schema: Option<String>,
    /// Path to a directory, where the compiler should write artifacts
    #[clap(long)]
    pub artifact_directory: Option<String>,
}

impl CliConfig {
    fn is_defined(&self) -> bool {
        self.src.is_some() || self.schema.is_some() || self.artifact_directory.is_some()
    }

    fn get_config_string(self) -> String {
        let src = self.src.unwrap_or_else(|| "./src".into());
        let schema = self.schema.unwrap_or_else(|| "./path-to-schema".into());
        let artifact_directory = self.artifact_directory.map_or("".to_string(), |a| {
            format!("\n  \"artifactDirectory\": \"{}\",", a)
        });
        format!(
            r#"
 {{
   "src": "{}",
   "schema": "{}",{}
   "language": "javascript"
 }}"#,
            src, schema, artifact_directory
        )
    }
}

#[tokio::main]
async fn main() {
    let opt = Opt::parse();

    let command = opt.command.unwrap_or(Commands::Compiler(opt.compile));

    let result = match command {
        Commands::Compiler(command) => handle_compiler_command(command).await,
        Commands::Lsp(command) => handle_lsp_command(command).await,
    };

    match result {
        Ok(_) => info!("Done."),
        Err(err) => {
            error!("{:?}", err);
            std::process::exit(1);
        }
    }
}

fn get_config(config_path: Option<PathBuf>) -> Result<Config, Error> {
    match config_path {
        Some(config_path) => Config::load(config_path).map_err(|err| Error::ConfigError {
            details: format!("{:?}", err),
        }),
        None => Config::search(&current_dir().expect("Unable to get current working directory."))
            .map_err(|err| Error::ConfigError {
                details: format!("{:?}", err),
            }),
    }
}

fn configure_logger(output: OutputKind, terminal_mode: TerminalMode) {
    let log_level = match output {
        OutputKind::Debug => LevelFilter::Debug,
        OutputKind::Quiet => LevelFilter::Off,
        OutputKind::QuietWithErrors => LevelFilter::Error,
        OutputKind::Verbose => LevelFilter::Info,
    };

    let log_config = SimpleLogConfigBuilder::new()
        .set_time_level(LevelFilter::Off)
        .set_target_level(LevelFilter::Off)
        .set_location_level(LevelFilter::Off)
        .set_thread_level(LevelFilter::Off)
        .build();

    TermLogger::init(log_level, log_config, terminal_mode, ColorChoice::Auto).unwrap();
}

async fn handle_compiler_command(command: CompileCommand) -> Result<(), Error> {
    configure_logger(command.output, TerminalMode::Mixed);

    if command.cli_config.is_defined() {
        return Err(Error::ConfigError {
            details: format!(
                "\nPassing Relay compiler configuration is not supported. Please add `relay.config.json` file,\nor \"relay\" section to your `package.json` file.\n\nCompiler configuration JSON:{}",
                command.cli_config.get_config_string(),
            ),
        });
    }

    let mut config = get_config(command.config)?;

    if command.validate {
        config.artifact_writer = Box::new(ArtifactValidationWriter::default());
    }

    config.create_operation_persister = Some(Box::new(|project_config| {
        project_config.persist.as_ref().map(
            |persist_config| -> Box<dyn OperationPersister + Send + Sync> {
                match persist_config {
                    PersistConfig::Remote(remote_config) => {
                        Box::new(RemotePersister::new(remote_config.clone()))
                    }
                    PersistConfig::Local(local_config) => {
                        Box::new(LocalPersister::new(local_config.clone()))
                    }
                }
            },
        )
    }));

    config.file_source_config = if should_use_watchman() {
        FileSourceKind::Watchman
    } else {
        FileSourceKind::WalkDir
    };
    config.repersist_operations = command.repersist;

    if command.watch && !matches!(&config.file_source_config, FileSourceKind::Watchman) {
        panic!(
            "Cannot run relay in watch mode if `watchman` is not available (or explicitly disabled)."
        );
    }

    let compiler = Compiler::new(Arc::new(config), Arc::new(ConsoleLogger));

    if command.watch {
        compiler.watch().await.map_err(|err| Error::CompilerError {
            details: format!("{:?}", err),
        })?;
    } else {
        compiler
            .compile()
            .await
            .map_err(|err| Error::CompilerError {
                details: format!("{:?}", err),
            })?;
    }

    Ok(())
}

async fn handle_lsp_command(command: LspCommand) -> Result<(), Error> {
    configure_logger(command.output, TerminalMode::Stderr);

    let config = get_config(command.config)?;

    let perf_logger = Arc::new(ConsoleLogger);
    let extra_data_provider = Box::new(DummyExtraDataProvider::new());
    let schema_documentation_loader: Option<Box<dyn SchemaDocumentationLoader<SDLSchema>>> = None;
    let js_language_server = None;

    start_language_server(
        config,
        perf_logger,
        extra_data_provider,
        schema_documentation_loader,
        js_language_server,
    )
    .await
    .map_err(|err| Error::LSPError {
        details: format!("Relay LSP unexpectedly terminated: {:?}", err),
    })?;

    info!("Relay LSP exited successfully.");

    Ok(())
}

/// Check if `watchman` is available.
/// Additionally, this method is checking for an existence of `FORCE_NO_WATCHMAN`
/// environment variable. If this `FORCE_NO_WATCHMAN` is set, this method will return `false`
/// and compiler will use non-watchman file finder.
fn should_use_watchman() -> bool {
    let check_watchman = Command::new("watchman")
        .args(["list-capabilities"])
        .output();

    check_watchman.is_ok() && env::var("FORCE_NO_WATCHMAN").is_err()
}
<|endoftext|>


<commit_before>//! List of the removed feature gates.

use super::{to_nonzero, Feature, State};
use rustc_span::symbol::sym;

macro_rules! declare_features {
    ($(
        $(#[doc = $doc:tt])* (removed, $feature:ident, $ver:expr, $issue:expr, None, $reason:expr),
    )+) => {
        /// Represents unstable features which have since been removed (it was once Active)
        pub const REMOVED_FEATURES: &[Feature] = &[
            $(
                Feature {
                    state: State::Removed { reason: $reason },
                    name: sym::$feature,
                    since: $ver,
                    issue: to_nonzero($issue),
                    edition: None,
                    description: concat!($($doc,)*),
                }
            ),+
        ];
    };

    ($(
        $(#[doc = $doc:tt])* (stable_removed, $feature:ident, $ver:expr, $issue:expr, None),
    )+) => {
        /// Represents stable features which have since been removed (it was once Accepted)
        pub const STABLE_REMOVED_FEATURES: &[Feature] = &[
            $(
                Feature {
                    state: State::Stabilized { reason: None },
                    name: sym::$feature,
                    since: $ver,
                    issue: to_nonzero($issue),
                    edition: None,
                    description: concat!($($doc,)*),
                }
            ),+
        ];
    };
}

#[rustfmt::skip]
declare_features! (
    // -------------------------------------------------------------------------
    // feature-group-start: removed features
    // -------------------------------------------------------------------------

    (removed, import_shadowing, "1.0.0", None, None, None),
    (removed, managed_boxes, "1.0.0", None, None, None),
    /// Allows use of unary negate on unsigned integers, e.g., -e for e: u8
    (removed, negate_unsigned, "1.0.0", Some(29645), None, None),
    (removed, reflect, "1.0.0", Some(27749), None, None),
    /// A way to temporarily opt out of opt in copy. This will *never* be accepted.
    (removed, opt_out_copy, "1.0.0", None, None, None),
    (removed, quad_precision_float, "1.0.0", None, None, None),
    (removed, struct_inherit, "1.0.0", None, None, None),
    (removed, test_removed_feature, "1.0.0", None, None, None),
    (removed, visible_private_types, "1.0.0", None, None, None),
    (removed, unsafe_no_drop_flag, "1.0.0", None, None, None),
    /// Allows using items which are missing stability attributes
    (removed, unmarked_api, "1.0.0", None, None, None),
    (removed, allocator, "1.0.0", None, None, None),
    (removed, simd, "1.0.0", Some(27731), None,
     Some("removed in favor of `#[repr(simd)]`")),
    (removed, advanced_slice_patterns, "1.0.0", Some(62254), None,
     Some("merged into `#![feature(slice_patterns)]`")),
    (removed, macro_reexport, "1.0.0", Some(29638), None,
     Some("subsumed by `pub use`")),
    /// Allows using custom attributes (RFC 572).
    (removed, custom_attribute, "1.0.0", Some(29642), None,
     Some("removed in favor of `#![register_tool]` and `#![register_attr]`")),
    /// Allows features specific to OIBIT (now called auto traits).
    /// Renamed to `auto_traits`.
    (removed, optin_builtin_traits, "1.0.0", Some(13231), None,
     Some("renamed to `auto_traits`")),
    (removed, pushpop_unsafe, "1.2.0", None, None, None),
    (removed, needs_allocator, "1.4.0", Some(27389), None,
     Some("subsumed by `#![feature(allocator_internals)]`")),
    /// Allows identifying crates that contain sanitizer runtimes.
    (removed, sanitizer_runtime, "1.17.0", None, None, None),
    /// Allows `#[doc(spotlight)]`.
    /// The attribute was renamed to `#[doc(notable_trait)]`
    /// and the feature to `doc_notable_trait`.
    (removed, doc_spotlight, "1.22.0", Some(45040), None,
     Some("renamed to `doc_notable_trait`")),
    (removed, proc_macro_mod, "1.27.0", Some(54727), None,
     Some("subsumed by `#![feature(proc_macro_hygiene)]`")),
    (removed, proc_macro_expr, "1.27.0", Some(54727), None,
     Some("subsumed by `#![feature(proc_macro_hygiene)]`")),
    (removed, proc_macro_non_items, "1.27.0", Some(54727), None,
     Some("subsumed by `#![feature(proc_macro_hygiene)]`")),
    (removed, proc_macro_gen, "1.27.0", Some(54727), None,
     Some("subsumed by `#![feature(proc_macro_hygiene)]`")),
    (removed, panic_implementation, "1.28.0", Some(44489), None,
     Some("subsumed by `#[panic_handler]`")),
    /// Allows the use of `#[derive(Anything)]` as sugar for `#[derive_Anything]`.
    (removed, custom_derive, "1.32.0", Some(29644), None,
     Some("subsumed by `#[proc_macro_derive]`")),
    /// Paths of the form: `extern::foo::bar`
    (removed, extern_in_paths, "1.33.0", Some(55600), None,
     Some("subsumed by `::foo::bar` paths")),
    (removed, quote, "1.33.0", Some(29601), None, None),
    /// Allows `[x; N]` where `x` is a constant (RFC 2203).
    (removed, const_in_array_repeat_expressions,  "1.37.0", Some(49147), None,
     Some("removed due to causing promotable bugs")),
    /// Allows using `#[unsafe_destructor_blind_to_params]` (RFC 1238).
    (removed, dropck_parametricity, "1.38.0", Some(28498), None, None),
    (removed, await_macro, "1.38.0", Some(50547), None,
     Some("subsumed by `.await` syntax")),
    /// Allows defining `existential type`s.
    (removed, existential_type, "1.38.0", Some(63063), None,
     Some("removed in favor of `#![feature(type_alias_impl_trait)]`")),
    /// Allows using the macros:
    /// + `__diagnostic_used`
    /// + `__register_diagnostic`
    /// +`__build_diagnostic_array`
    (removed, rustc_diagnostic_macros, "1.38.0", None, None, None),
    /// Allows using `#[on_unimplemented(..)]` on traits.
    /// (Moved to `rustc_attrs`.)
    (removed, on_unimplemented, "1.40.0", None, None, None),
    /// Allows overlapping impls of marker traits.
    (removed, overlapping_marker_traits, "1.42.0", Some(29864), None,
     Some("removed in favor of `#![feature(marker_trait_attr)]`")),
    /// Allows `#[no_debug]`.
    (removed, no_debug, "1.43.0", Some(29721), None, Some("removed due to lack of demand")),
    /// Allows comparing raw pointers during const eval.
    (removed, const_compare_raw_pointers, "1.46.0", Some(53020), None,
     Some("cannot be allowed in const eval in any meaningful way")),
    /// Allows using the `#[link_args]` attribute.
    (removed, link_args, "1.53.0", Some(29596), None,
     Some("removed in favor of using `-C link-arg=ARG` on command line, \
           which is available from cargo build scripts with `cargo:rustc-link-arg` now")),
    /// Allows using `#[main]` to replace the entrypoint `#[lang = "start"]` calls.
    (removed, main, "1.53.0", Some(29634), None, None),
    (removed, pub_macro_rules, "1.53.0", Some(78855), None,
     Some("removed due to being incomplete, in particular it does not work across crates")),
     /// Allows the definition of `const` functions with some advanced features.
    (removed, const_fn, "1.54.0", Some(57563), None,
     Some("split into finer-grained feature gates")),

    /// Allows `#[doc(include = "some-file")]`.
    (removed, external_doc, "1.54.0", Some(44732), None,
     Some("use #[doc = include_str!(\"filename\")] instead, which handles macro invocations")),

     /// Allows casting raw pointers to `usize` during const eval.
    (removed, const_raw_ptr_to_usize_cast, "1.55.0", Some(51910), None,
     Some("at compile-time, pointers do not have an integer value, so these casts cannot be properly supported")),

    /// Allows `impl Trait` in bindings (`let`, `const`, `static`).
    (removed, impl_trait_in_bindings, "1.55.0", Some(63065), None,
     Some("the implementation was not maintainable, the feature may get reintroduced once the current refactorings are done")),

    /// Allows the use of type alias impl trait in function return positions
    (removed, min_type_alias_impl_trait, "1.55.0", Some(63063), None,
     Some("removed in favor of full type_alias_impl_trait")),

    // -------------------------------------------------------------------------
    // feature-group-end: removed features
    // -------------------------------------------------------------------------
);

#[rustfmt::skip]
declare_features! (
    (stable_removed, no_stack_check, "1.0.0", None, None),
);
<commit_msg>min_type_alias_impl_trait is going to be removed in 1.56<commit_after>//! List of the removed feature gates.

use super::{to_nonzero, Feature, State};
use rustc_span::symbol::sym;

macro_rules! declare_features {
    ($(
        $(#[doc = $doc:tt])* (removed, $feature:ident, $ver:expr, $issue:expr, None, $reason:expr),
    )+) => {
        /// Represents unstable features which have since been removed (it was once Active)
        pub const REMOVED_FEATURES: &[Feature] = &[
            $(
                Feature {
                    state: State::Removed { reason: $reason },
                    name: sym::$feature,
                    since: $ver,
                    issue: to_nonzero($issue),
                    edition: None,
                    description: concat!($($doc,)*),
                }
            ),+
        ];
    };

    ($(
        $(#[doc = $doc:tt])* (stable_removed, $feature:ident, $ver:expr, $issue:expr, None),
    )+) => {
        /// Represents stable features which have since been removed (it was once Accepted)
        pub const STABLE_REMOVED_FEATURES: &[Feature] = &[
            $(
                Feature {
                    state: State::Stabilized { reason: None },
                    name: sym::$feature,
                    since: $ver,
                    issue: to_nonzero($issue),
                    edition: None,
                    description: concat!($($doc,)*),
                }
            ),+
        ];
    };
}

#[rustfmt::skip]
declare_features! (
    // -------------------------------------------------------------------------
    // feature-group-start: removed features
    // -------------------------------------------------------------------------

    (removed, import_shadowing, "1.0.0", None, None, None),
    (removed, managed_boxes, "1.0.0", None, None, None),
    /// Allows use of unary negate on unsigned integers, e.g., -e for e: u8
    (removed, negate_unsigned, "1.0.0", Some(29645), None, None),
    (removed, reflect, "1.0.0", Some(27749), None, None),
    /// A way to temporarily opt out of opt in copy. This will *never* be accepted.
    (removed, opt_out_copy, "1.0.0", None, None, None),
    (removed, quad_precision_float, "1.0.0", None, None, None),
    (removed, struct_inherit, "1.0.0", None, None, None),
    (removed, test_removed_feature, "1.0.0", None, None, None),
    (removed, visible_private_types, "1.0.0", None, None, None),
    (removed, unsafe_no_drop_flag, "1.0.0", None, None, None),
    /// Allows using items which are missing stability attributes
    (removed, unmarked_api, "1.0.0", None, None, None),
    (removed, allocator, "1.0.0", None, None, None),
    (removed, simd, "1.0.0", Some(27731), None,
     Some("removed in favor of `#[repr(simd)]`")),
    (removed, advanced_slice_patterns, "1.0.0", Some(62254), None,
     Some("merged into `#![feature(slice_patterns)]`")),
    (removed, macro_reexport, "1.0.0", Some(29638), None,
     Some("subsumed by `pub use`")),
    /// Allows using custom attributes (RFC 572).
    (removed, custom_attribute, "1.0.0", Some(29642), None,
     Some("removed in favor of `#![register_tool]` and `#![register_attr]`")),
    /// Allows features specific to OIBIT (now called auto traits).
    /// Renamed to `auto_traits`.
    (removed, optin_builtin_traits, "1.0.0", Some(13231), None,
     Some("renamed to `auto_traits`")),
    (removed, pushpop_unsafe, "1.2.0", None, None, None),
    (removed, needs_allocator, "1.4.0", Some(27389), None,
     Some("subsumed by `#![feature(allocator_internals)]`")),
    /// Allows identifying crates that contain sanitizer runtimes.
    (removed, sanitizer_runtime, "1.17.0", None, None, None),
    /// Allows `#[doc(spotlight)]`.
    /// The attribute was renamed to `#[doc(notable_trait)]`
    /// and the feature to `doc_notable_trait`.
    (removed, doc_spotlight, "1.22.0", Some(45040), None,
     Some("renamed to `doc_notable_trait`")),
    (removed, proc_macro_mod, "1.27.0", Some(54727), None,
     Some("subsumed by `#![feature(proc_macro_hygiene)]`")),
    (removed, proc_macro_expr, "1.27.0", Some(54727), None,
     Some("subsumed by `#![feature(proc_macro_hygiene)]`")),
    (removed, proc_macro_non_items, "1.27.0", Some(54727), None,
     Some("subsumed by `#![feature(proc_macro_hygiene)]`")),
    (removed, proc_macro_gen, "1.27.0", Some(54727), None,
     Some("subsumed by `#![feature(proc_macro_hygiene)]`")),
    (removed, panic_implementation, "1.28.0", Some(44489), None,
     Some("subsumed by `#[panic_handler]`")),
    /// Allows the use of `#[derive(Anything)]` as sugar for `#[derive_Anything]`.
    (removed, custom_derive, "1.32.0", Some(29644), None,
     Some("subsumed by `#[proc_macro_derive]`")),
    /// Paths of the form: `extern::foo::bar`
    (removed, extern_in_paths, "1.33.0", Some(55600), None,
     Some("subsumed by `::foo::bar` paths")),
    (removed, quote, "1.33.0", Some(29601), None, None),
    /// Allows `[x; N]` where `x` is a constant (RFC 2203).
    (removed, const_in_array_repeat_expressions,  "1.37.0", Some(49147), None,
     Some("removed due to causing promotable bugs")),
    /// Allows using `#[unsafe_destructor_blind_to_params]` (RFC 1238).
    (removed, dropck_parametricity, "1.38.0", Some(28498), None, None),
    (removed, await_macro, "1.38.0", Some(50547), None,
     Some("subsumed by `.await` syntax")),
    /// Allows defining `existential type`s.
    (removed, existential_type, "1.38.0", Some(63063), None,
     Some("removed in favor of `#![feature(type_alias_impl_trait)]`")),
    /// Allows using the macros:
    /// + `__diagnostic_used`
    /// + `__register_diagnostic`
    /// +`__build_diagnostic_array`
    (removed, rustc_diagnostic_macros, "1.38.0", None, None, None),
    /// Allows using `#[on_unimplemented(..)]` on traits.
    /// (Moved to `rustc_attrs`.)
    (removed, on_unimplemented, "1.40.0", None, None, None),
    /// Allows overlapping impls of marker traits.
    (removed, overlapping_marker_traits, "1.42.0", Some(29864), None,
     Some("removed in favor of `#![feature(marker_trait_attr)]`")),
    /// Allows `#[no_debug]`.
    (removed, no_debug, "1.43.0", Some(29721), None, Some("removed due to lack of demand")),
    /// Allows comparing raw pointers during const eval.
    (removed, const_compare_raw_pointers, "1.46.0", Some(53020), None,
     Some("cannot be allowed in const eval in any meaningful way")),
    /// Allows using the `#[link_args]` attribute.
    (removed, link_args, "1.53.0", Some(29596), None,
     Some("removed in favor of using `-C link-arg=ARG` on command line, \
           which is available from cargo build scripts with `cargo:rustc-link-arg` now")),
    /// Allows using `#[main]` to replace the entrypoint `#[lang = "start"]` calls.
    (removed, main, "1.53.0", Some(29634), None, None),
    (removed, pub_macro_rules, "1.53.0", Some(78855), None,
     Some("removed due to being incomplete, in particular it does not work across crates")),
     /// Allows the definition of `const` functions with some advanced features.
    (removed, const_fn, "1.54.0", Some(57563), None,
     Some("split into finer-grained feature gates")),

    /// Allows `#[doc(include = "some-file")]`.
    (removed, external_doc, "1.54.0", Some(44732), None,
     Some("use #[doc = include_str!(\"filename\")] instead, which handles macro invocations")),

     /// Allows casting raw pointers to `usize` during const eval.
    (removed, const_raw_ptr_to_usize_cast, "1.55.0", Some(51910), None,
     Some("at compile-time, pointers do not have an integer value, so these casts cannot be properly supported")),

    /// Allows `impl Trait` in bindings (`let`, `const`, `static`).
    (removed, impl_trait_in_bindings, "1.55.0", Some(63065), None,
     Some("the implementation was not maintainable, the feature may get reintroduced once the current refactorings are done")),

    /// Allows the use of type alias impl trait in function return positions
    (removed, min_type_alias_impl_trait, "1.56.0", Some(63063), None,
     Some("removed in favor of full type_alias_impl_trait")),

    // -------------------------------------------------------------------------
    // feature-group-end: removed features
    // -------------------------------------------------------------------------
);

#[rustfmt::skip]
declare_features! (
    (stable_removed, no_stack_check, "1.0.0", None, None),
);
<|endoftext|>


<commit_before>extern crate forth;

use forth::{Error, Forth, Value};

#[test]
fn no_input_no_stack() {
    assert_eq!(Vec::<Value>::new(), Forth::new().stack());
}

#[test]
#[ignore]
fn numbers_just_get_pushed_onto_the_stack() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 3 4 5").is_ok());
    assert_eq!(vec![1, 2, 3, 4, 5], f.stack());
}

#[test]
#[ignore]
fn non_word_characters_are_separators() {
    let mut f = Forth::new();
    // Note the Ogham Space Mark ( ), this is a spacing character.
    assert!(f.eval("1\u{0000}2\u{0001}3\n4\r5 6\t7").is_ok());
    assert_eq!(vec![1, 2, 3, 4, 5, 6, 7], f.stack());
}

#[test]
#[ignore]
fn basic_arithmetic_1() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 + 4 -").is_ok());
    assert_eq!(vec![-1], f.stack());
}

#[test]
#[ignore]
fn basic_arithmetic_2() {
    let mut f = Forth::new();
    assert!(f.eval("2 4 * 3 /").is_ok());
    assert_eq!(vec![2], f.stack());
}

#[test]
#[ignore]
fn addition_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("+"));
}

#[test]
#[ignore]
fn subtraction_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("-"));
}

#[test]
#[ignore]
fn multiplication_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("*"));
}

#[test]
#[ignore]
fn division_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("/"));
}

#[test]
#[ignore]
fn division_by_zero() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::DivisionByZero), f.eval("4 2 2 - /"));
}

#[test]
#[ignore]
fn dup() {
    let mut f = Forth::new();
    assert!(f.eval("1 DUP").is_ok());
    assert_eq!(vec![1, 1], f.stack());
}

#[test]
#[ignore]
fn dup_case_insensitive() {
    let mut f = Forth::new();
    assert!(f.eval("1 Dup").is_ok());
    assert_eq!(vec![1, 1], f.stack());
}

#[test]
#[ignore]
fn dup_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("dup"));
}

#[test]
#[ignore]
fn drop() {
    let mut f = Forth::new();
    assert!(f.eval("1 drop").is_ok());
    assert_eq!(Vec::<Value>::new(), f.stack());
}

#[test]
#[ignore]
fn drop_with_two() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 drop").is_ok());
    assert_eq!(vec![1], f.stack());
}

#[test]
#[ignore]
fn drop_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("drop"));
}

#[test]
#[ignore]
fn swap() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 swap").is_ok());
    assert_eq!(vec![2, 1], f.stack());
}

#[test]
#[ignore]
fn swap_with_three() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 3 swap").is_ok());
    assert_eq!(vec![1, 3, 2], f.stack());
}

#[test]
#[ignore]
fn swap_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("1 swap"));
    assert_eq!(Err(Error::StackUnderflow), f.eval("swap"));
}

#[test]
#[ignore]
fn over() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 over").is_ok());
    assert_eq!(vec![1, 2, 1], f.stack());
}

#[test]
#[ignore]
fn over_with_three() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 3 over").is_ok());
    assert_eq!(vec![1, 2, 3, 2], f.stack());
}

#[test]
#[ignore]
fn over_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("1 over"));
    assert_eq!(Err(Error::StackUnderflow), f.eval("over"));
}

#[test]
#[ignore]
fn defining_a_new_word() {
    let mut f = Forth::new();
    assert!(f.eval(": CoUnT 1 2 3 ;").is_ok());
    assert!(f.eval("count COUNT").is_ok());
    assert_eq!(vec![1, 2, 3, 1, 2, 3], f.stack());
}

#[test]
#[ignore]
fn redefining_an_existing_word() {
    let mut f = Forth::new();
    assert!(f.eval(": foo dup ;").is_ok());
    assert!(f.eval(": foo dup dup ;").is_ok());
    assert!(f.eval("1 foo").is_ok());
    assert_eq!(vec![1, 1, 1], f.stack());
}

#[test]
#[ignore]
fn redefining_an_existing_built_in_word() {
    let mut f = Forth::new();
    assert!(f.eval(": swap dup ;").is_ok());
    assert!(f.eval("1 swap").is_ok());
    assert_eq!(vec![1, 1], f.stack());
}

#[test]
#[ignore]
fn defining_words_with_odd_characters() {
    let mut f = Forth::new();
    assert!(f.eval(": € 220371 ; €").is_ok());
    assert_eq!(vec![220371], f.stack());
}

#[test]
#[ignore]
fn defining_a_number() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::InvalidWord), f.eval(": 1 2 ;"));
}

#[test]
#[ignore]
fn malformed_word_definition() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::InvalidWord), f.eval(":"));
    assert_eq!(Err(Error::InvalidWord), f.eval(": foo"));
    assert_eq!(Err(Error::InvalidWord), f.eval(": foo 1"));
}

#[test]
#[ignore]
fn calling_non_existing_word() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::UnknownWord), f.eval("1 foo"));
}
<commit_msg>forth: rm Ogham space<commit_after>extern crate forth;

use forth::{Error, Forth, Value};

#[test]
fn no_input_no_stack() {
    assert_eq!(Vec::<Value>::new(), Forth::new().stack());
}

#[test]
#[ignore]
fn numbers_just_get_pushed_onto_the_stack() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 3 4 5").is_ok());
    assert_eq!(vec![1, 2, 3, 4, 5], f.stack());
}

#[test]
#[ignore]
fn non_word_characters_are_separators() {
    let mut f = Forth::new();
    assert!(f.eval("1\u{0000}2\u{0001}3\n4\r5 6\t7").is_ok());
    assert_eq!(vec![1, 2, 3, 4, 5, 6, 7], f.stack());
}

#[test]
#[ignore]
fn basic_arithmetic_1() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 + 4 -").is_ok());
    assert_eq!(vec![-1], f.stack());
}

#[test]
#[ignore]
fn basic_arithmetic_2() {
    let mut f = Forth::new();
    assert!(f.eval("2 4 * 3 /").is_ok());
    assert_eq!(vec![2], f.stack());
}

#[test]
#[ignore]
fn addition_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("+"));
}

#[test]
#[ignore]
fn subtraction_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("-"));
}

#[test]
#[ignore]
fn multiplication_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("*"));
}

#[test]
#[ignore]
fn division_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("/"));
}

#[test]
#[ignore]
fn division_by_zero() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::DivisionByZero), f.eval("4 2 2 - /"));
}

#[test]
#[ignore]
fn dup() {
    let mut f = Forth::new();
    assert!(f.eval("1 DUP").is_ok());
    assert_eq!(vec![1, 1], f.stack());
}

#[test]
#[ignore]
fn dup_case_insensitive() {
    let mut f = Forth::new();
    assert!(f.eval("1 Dup").is_ok());
    assert_eq!(vec![1, 1], f.stack());
}

#[test]
#[ignore]
fn dup_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("dup"));
}

#[test]
#[ignore]
fn drop() {
    let mut f = Forth::new();
    assert!(f.eval("1 drop").is_ok());
    assert_eq!(Vec::<Value>::new(), f.stack());
}

#[test]
#[ignore]
fn drop_with_two() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 drop").is_ok());
    assert_eq!(vec![1], f.stack());
}

#[test]
#[ignore]
fn drop_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("drop"));
}

#[test]
#[ignore]
fn swap() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 swap").is_ok());
    assert_eq!(vec![2, 1], f.stack());
}

#[test]
#[ignore]
fn swap_with_three() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 3 swap").is_ok());
    assert_eq!(vec![1, 3, 2], f.stack());
}

#[test]
#[ignore]
fn swap_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("1 swap"));
    assert_eq!(Err(Error::StackUnderflow), f.eval("swap"));
}

#[test]
#[ignore]
fn over() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 over").is_ok());
    assert_eq!(vec![1, 2, 1], f.stack());
}

#[test]
#[ignore]
fn over_with_three() {
    let mut f = Forth::new();
    assert!(f.eval("1 2 3 over").is_ok());
    assert_eq!(vec![1, 2, 3, 2], f.stack());
}

#[test]
#[ignore]
fn over_error() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::StackUnderflow), f.eval("1 over"));
    assert_eq!(Err(Error::StackUnderflow), f.eval("over"));
}

#[test]
#[ignore]
fn defining_a_new_word() {
    let mut f = Forth::new();
    assert!(f.eval(": CoUnT 1 2 3 ;").is_ok());
    assert!(f.eval("count COUNT").is_ok());
    assert_eq!(vec![1, 2, 3, 1, 2, 3], f.stack());
}

#[test]
#[ignore]
fn redefining_an_existing_word() {
    let mut f = Forth::new();
    assert!(f.eval(": foo dup ;").is_ok());
    assert!(f.eval(": foo dup dup ;").is_ok());
    assert!(f.eval("1 foo").is_ok());
    assert_eq!(vec![1, 1, 1], f.stack());
}

#[test]
#[ignore]
fn redefining_an_existing_built_in_word() {
    let mut f = Forth::new();
    assert!(f.eval(": swap dup ;").is_ok());
    assert!(f.eval("1 swap").is_ok());
    assert_eq!(vec![1, 1], f.stack());
}

#[test]
#[ignore]
fn defining_words_with_odd_characters() {
    let mut f = Forth::new();
    assert!(f.eval(": € 220371 ; €").is_ok());
    assert_eq!(vec![220371], f.stack());
}

#[test]
#[ignore]
fn defining_a_number() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::InvalidWord), f.eval(": 1 2 ;"));
}

#[test]
#[ignore]
fn malformed_word_definition() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::InvalidWord), f.eval(":"));
    assert_eq!(Err(Error::InvalidWord), f.eval(": foo"));
    assert_eq!(Err(Error::InvalidWord), f.eval(": foo 1"));
}

#[test]
#[ignore]
fn calling_non_existing_word() {
    let mut f = Forth::new();
    assert_eq!(Err(Error::UnknownWord), f.eval("1 foo"));
}
<|endoftext|>


<commit_before>
use wtest_basic::*;

#[cfg( feature = "in_wtools" )]
use wtools::string as TheModule;
#[cfg( not( feature = "in_wtools" ) )]
use wstring_tools as TheModule;

// use TheModule::prelude::*;

//

fn _basic()
{
  let src = "abc";
  let iter = TheModule::string::split()
  .src( src )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "", "", "a", "", "b", "", "c", "", "", ] );
}

//

fn _split_with_option_preserving_empty()
{
  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .preserving_empty( true )
  .stripping( false )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", " ", "b", " ", "c" ] );

  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .preserving_empty( false )
  .stripping( false )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", " ", "b", " ", "c" ] );

  /* */

  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .preserving_empty( true )
  .stripping( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "", "b", "", "c" ] );

  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .preserving_empty( false )
  .stripping( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "b", "c" ] );
}

//

fn _split_with_option_preserving_delimeters()
{
  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .preserving_delimeters( true )
  .stripping( false )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", " ", "b", " ", "c" ] );

  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .preserving_delimeters( false )
  .stripping( false )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "b", "c" ] );
}

//

fn _split_with_option_stripping()
{
  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .stripping( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "", "b", "", "c" ] );

  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .stripping( false )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", " ", "b", " ", "c" ] );
}

//

test_suite!
{
  basic,
  split_with_option_preserving_empty,
  split_with_option_preserving_delimeters,
  split_with_option_stripping,
}
<commit_msg>Extend test routine `split_with_option_stripping`, add test cases for stripped parts<commit_after>
use wtest_basic::*;

#[cfg( feature = "in_wtools" )]
use wtools::string as TheModule;
#[cfg( not( feature = "in_wtools" ) )]
use wstring_tools as TheModule;

// use TheModule::prelude::*;

//

fn _basic()
{
  let src = "abc";
  let iter = TheModule::string::split()
  .src( src )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "", "", "a", "", "b", "", "c", "", "", ] );
}

//

fn _split_with_option_preserving_empty()
{
  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .preserving_empty( true )
  .stripping( false )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", " ", "b", " ", "c" ] );

  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .preserving_empty( false )
  .stripping( false )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", " ", "b", " ", "c" ] );

  /* */

  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .preserving_empty( true )
  .stripping( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "", "b", "", "c" ] );

  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .preserving_empty( false )
  .stripping( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "b", "c" ] );
}

//

fn _split_with_option_preserving_delimeters()
{
  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .preserving_delimeters( true )
  .stripping( false )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", " ", "b", " ", "c" ] );

  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .preserving_delimeters( false )
  .stripping( false )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "b", "c" ] );
}

//

fn _split_with_option_stripping()
{
  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .stripping( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "", "b", "", "c" ] );

  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( " " )
  .stripping( false )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", " ", "b", " ", "c" ] );

  /* */

  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( "b" )
  .stripping( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "b", "c" ] );

  let src = "a b c";
  let iter = TheModule::string::split()
  .src( src )
  .delimeter( "b" )
  .preserving_delimeters( false )
  .stripping( true )
  .perform();
  assert_eq!( iter.map( | e | String::from( e ) ).collect::< Vec< _ > >(), vec![ "a", "c" ] );
}

//

test_suite!
{
  basic,
  split_with_option_preserving_empty,
  split_with_option_preserving_delimeters,
  split_with_option_stripping,
}
<|endoftext|>


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

use core::prelude::*;

use driver::session::Session;

use syntax::ast;
use syntax::attr;
use syntax::codemap;
use syntax::codemap::dummy_sp;
use syntax::fold;

static STD_VERSION: &'static str = "0.7-pre";

pub fn maybe_inject_libstd_ref(sess: Session, crate: @ast::crate)
                               -> @ast::crate {
    if use_std(crate) {
        inject_libstd_ref(sess, crate)
    } else {
        crate
    }
}

fn use_std(crate: @ast::crate) -> bool {
    !attr::attrs_contains_name(crate.node.attrs, "no_std")
}

fn inject_libstd_ref(sess: Session, crate: @ast::crate) -> @ast::crate {
    fn spanned<T:Copy>(x: T) -> codemap::spanned<T> {
        codemap::spanned { node: x, span: dummy_sp() }
    }

    let precursor = @fold::AstFoldFns {
        fold_crate: |crate, span, fld| {
            let n1 = sess.next_node_id();
            let vi1 = @ast::view_item {
                node: ast::view_item_extern_mod(
                        sess.ident_of("core"), ~[], n1),
                attrs: ~[
                    spanned(ast::attribute_ {
                        style: ast::attr_inner,
                        value: @spanned(ast::meta_name_value(
                            @~"vers",
                            spanned(ast::lit_str(@STD_VERSION.to_str()))
                        )),
                        is_sugared_doc: false
                    })
                ],
                vis: ast::private,
                span: dummy_sp()
            };

            let vis = vec::append(~[vi1], crate.module.view_items);
            let mut new_module = ast::_mod {
                view_items: vis,
                ../*bad*/copy crate.module
            };
            new_module = fld.fold_mod(&new_module);

            // FIXME #2543: Bad copy.
            let new_crate = ast::crate_ {
                module: new_module,
                ..copy *crate
            };
            (new_crate, span)
        },
        fold_mod: |module, fld| {
            let n2 = sess.next_node_id();

            let prelude_path = @ast::Path {
                span: dummy_sp(),
                global: false,
                idents: ~[
                    sess.ident_of("core"),
                    sess.ident_of("prelude")
                ],
                rp: None,
                types: ~[]
            };

            let vp = @spanned(ast::view_path_glob(prelude_path, n2));
            let vi2 = @ast::view_item { node: ast::view_item_use(~[vp]),
                                        attrs: ~[],
                                        vis: ast::private,
                                        span: dummy_sp() };

            let vis = vec::append(~[vi2], module.view_items);

            // FIXME #2543: Bad copy.
            let new_module = ast::_mod {
                view_items: vis,
                ..copy *module
            };
            fold::noop_fold_mod(&new_module, fld)
        },
        ..*fold::default_ast_fold()
    };

    let fold = fold::make_fold(precursor);
    @fold.fold_crate(crate)
}
<commit_msg>librustc: Make `std_inject` inject the name `std`, not `core`<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use core::prelude::*;

use driver::session::Session;

use syntax::ast;
use syntax::attr;
use syntax::codemap;
use syntax::codemap::dummy_sp;
use syntax::fold;

static STD_VERSION: &'static str = "0.7-pre";

pub fn maybe_inject_libstd_ref(sess: Session, crate: @ast::crate)
                               -> @ast::crate {
    if use_std(crate) {
        inject_libstd_ref(sess, crate)
    } else {
        crate
    }
}

fn use_std(crate: @ast::crate) -> bool {
    !attr::attrs_contains_name(crate.node.attrs, "no_std")
}

fn inject_libstd_ref(sess: Session, crate: @ast::crate) -> @ast::crate {
    fn spanned<T:Copy>(x: T) -> codemap::spanned<T> {
        codemap::spanned { node: x, span: dummy_sp() }
    }

    let precursor = @fold::AstFoldFns {
        fold_crate: |crate, span, fld| {
            let n1 = sess.next_node_id();
            let vi1 = @ast::view_item {
                node: ast::view_item_extern_mod(
                        sess.ident_of("std"), ~[], n1),
                attrs: ~[
                    spanned(ast::attribute_ {
                        style: ast::attr_inner,
                        value: @spanned(ast::meta_name_value(
                            @~"vers",
                            spanned(ast::lit_str(@STD_VERSION.to_str()))
                        )),
                        is_sugared_doc: false
                    })
                ],
                vis: ast::private,
                span: dummy_sp()
            };

            let vis = vec::append(~[vi1], crate.module.view_items);
            let mut new_module = ast::_mod {
                view_items: vis,
                ../*bad*/copy crate.module
            };
            new_module = fld.fold_mod(&new_module);

            // FIXME #2543: Bad copy.
            let new_crate = ast::crate_ {
                module: new_module,
                ..copy *crate
            };
            (new_crate, span)
        },
        fold_mod: |module, fld| {
            let n2 = sess.next_node_id();

            let prelude_path = @ast::Path {
                span: dummy_sp(),
                global: false,
                idents: ~[
                    sess.ident_of("std"),
                    sess.ident_of("prelude")
                ],
                rp: None,
                types: ~[]
            };

            let vp = @spanned(ast::view_path_glob(prelude_path, n2));
            let vi2 = @ast::view_item { node: ast::view_item_use(~[vp]),
                                        attrs: ~[],
                                        vis: ast::private,
                                        span: dummy_sp() };

            let vis = vec::append(~[vi2], module.view_items);

            // FIXME #2543: Bad copy.
            let new_module = ast::_mod {
                view_items: vis,
                ..copy *module
            };
            fold::noop_fold_mod(&new_module, fld)
        },
        ..*fold::default_ast_fold()
    };

    let fold = fold::make_fold(precursor);
    @fold.fold_crate(crate)
}
<|endoftext|>


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

use core::libc::c_ulonglong;
use core::option::{Option, Some, None};
use core::vec;
use lib::llvm::{ValueRef, TypeRef, True, False};
use middle::trans::_match;
use middle::trans::build::*;
use middle::trans::common::*;
use middle::trans::machine;
use middle::trans::type_of;
use middle::ty;
use syntax::ast;
use util::ppaux::ty_to_str;

// XXX: should this be done with boxed traits instead of ML-style?
pub enum Repr {
    Unit(int),
    CEnum(int, int), /* discriminant range */
    Univariant(Struct, Destructor),
    General(~[Struct])
}

enum Destructor {
    DtorPresent,
    DtorAbsent,
    NoDtor
}

struct Struct {
    size: u64,
    align: u64,
    fields: ~[ty::t]
}

pub fn represent_node(bcx: block, node: ast::node_id)
    -> Repr {
    represent_type(bcx.ccx(), node_id_type(bcx, node))
}

pub fn represent_type(cx: @CrateContext, t: ty::t) -> Repr {
    debug!("Representing: %s", ty_to_str(cx.tcx, t));
    // XXX: cache this
    match ty::get(t).sty {
        ty::ty_tup(ref elems) => {
            Univariant(mk_struct(cx, *elems), NoDtor)
        }
        ty::ty_rec(ref fields) => {
            // XXX: Are these in the right order?
            Univariant(mk_struct(cx, fields.map(|f| f.mt.ty)), DtorAbsent)
        }
        ty::ty_struct(def_id, ref substs) => {
            let fields = ty::lookup_struct_fields(cx.tcx, def_id);
            let dt = ty::ty_dtor(cx.tcx, def_id).is_present();
            Univariant(mk_struct(cx, fields.map(|field| {
                ty::lookup_field_type(cx.tcx, def_id, field.id, substs)
            })), if dt { DtorPresent } else { DtorAbsent })
        }
        ty::ty_enum(def_id, ref substs) => {
            struct Case { discr: int, tys: ~[ty::t] };

            let cases = do ty::enum_variants(cx.tcx, def_id).map |vi| {
                let arg_tys = do vi.args.map |&raw_ty| {
                    ty::subst(cx.tcx, substs, raw_ty)
                };
                Case { discr: vi.disr_val, tys: arg_tys }
            };
            if cases.len() == 0 {
                // Uninhabitable; represent as unit
                Univariant(mk_struct(cx, ~[]), NoDtor)
            } else if cases.len() == 1 && cases[0].tys.len() == 0 {
                Unit(cases[0].discr)
            } else if cases.len() == 1 {
                // struct, tuple, newtype, etc.
                assert cases[0].discr == 0;
                Univariant(mk_struct(cx, cases[0].tys), NoDtor)
            } else if cases.all(|c| c.tys.len() == 0) {
                let discrs = cases.map(|c| c.discr);
                CEnum(discrs.min(), discrs.max())
            } else {
                if !cases.alli(|i,c| c.discr == (i as int)) {
                    cx.sess.bug(fmt!("non-C-like enum %s with specified \
                                      discriminants",
                                     ty::item_path_str(cx.tcx, def_id)))
                }
                General(cases.map(|c| mk_struct(cx, c.tys)))
            }
        }
        _ => cx.sess.bug(~"adt::represent_type called on non-ADT type")
    }
}

fn mk_struct(cx: @CrateContext, tys: &[ty::t]) -> Struct {
    let lltys = tys.map(|&ty| type_of::sizing_type_of(cx, ty));
    let llty_rec = T_struct(lltys);
    Struct {
        size: machine::llsize_of_alloc(cx, llty_rec) /*bad*/as u64,
        align: machine::llalign_of_min(cx, llty_rec) /*bad*/as u64,
        fields: vec::from_slice(tys)
    }
}

pub fn sizing_fields_of(cx: @CrateContext, r: &Repr) -> ~[TypeRef] {
    generic_fields_of(cx, r, true)
}
pub fn fields_of(cx: @CrateContext, r: &Repr) -> ~[TypeRef] {
    generic_fields_of(cx, r, false)
}
fn generic_fields_of(cx: @CrateContext, r: &Repr, sizing: bool)
    -> ~[TypeRef] {
    match *r {
        Unit(*) => ~[],
        CEnum(*) => ~[T_enum_discrim(cx)],
        Univariant(ref st, dt) => {
            let f = if sizing {
                st.fields.map(|&ty| type_of::sizing_type_of(cx, ty))
            } else {
                st.fields.map(|&ty| type_of::type_of(cx, ty))
            };
            match dt {
                NoDtor => f,
                DtorAbsent => ~[T_struct(f)],
                DtorPresent => ~[T_struct(f), T_i8()]
            }
        }
        General(ref sts) => {
            ~[T_enum_discrim(cx),
              T_array(T_i8(), sts.map(|st| st.size).max() /*bad*/as uint)]
        }
    }
}

fn load_discr(bcx: block, scrutinee: ValueRef, min: int, max: int)
    -> ValueRef {
    let ptr = GEPi(bcx, scrutinee, [0, 0]);
    if max + 1 == min {
        // i.e., if the range is everything.  The lo==hi case would be
        // rejected by the LLVM verifier (it would mean either an
        // empty set, which is impossible, or the entire range of the
        // type, which is pointless).
        Load(bcx, ptr)
    } else {
        // llvm::ConstantRange can deal with ranges that wrap around,
        // so an overflow on (max + 1) is fine.
        LoadRangeAssert(bcx, ptr, min as c_ulonglong,
                        (max + 1) as c_ulonglong,
                        /* signed: */ True)
    }
}

pub fn trans_switch(bcx: block, r: &Repr, scrutinee: ValueRef) ->
    (_match::branch_kind, Option<ValueRef>) {
    match *r {
        CEnum(min, max) => {
            (_match::switch, Some(load_discr(bcx, scrutinee, min, max)))
        }
        Unit(*) | Univariant(*) => {
            (_match::single, None)
        }
        General(ref cases) => {
            (_match::switch, Some(load_discr(bcx, scrutinee, 0,
                                             (cases.len() - 1) as int)))
        }
    }
}

pub fn trans_case(bcx: block, r: &Repr, discr: int) -> _match::opt_result {
    match *r {
        CEnum(*) => {
            _match::single_result(rslt(bcx, C_int(bcx.ccx(), discr)))
        }
        Unit(*) | Univariant(*)=> {
            bcx.ccx().sess.bug(~"no cases for univariants or structs")
        }
        General(*) => {
            _match::single_result(rslt(bcx, C_int(bcx.ccx(), discr)))
        }
    }
}

pub fn trans_set_discr(bcx: block, r: &Repr, val: ValueRef, discr: int) {
    match *r {
        Unit(the_discr) => {
            assert discr == the_discr;
        }
        CEnum(min, max) => {
            assert min <= discr && discr <= max;
            Store(bcx, C_int(bcx.ccx(), discr), GEPi(bcx, val, [0, 0]))
        }
        Univariant(_, DtorPresent) => {
            assert discr == 0;
            Store(bcx, C_u8(1), GEPi(bcx, val, [0, 1]))
        }
        Univariant(*) => {
            assert discr == 0;
        }
        General(*) => {
            Store(bcx, C_int(bcx.ccx(), discr), GEPi(bcx, val, [0, 0]))
        }
    }
}

pub fn num_args(r: &Repr, discr: int) -> uint {
    match *r {
        Unit(*) | CEnum(*) => 0,
        Univariant(ref st, _dt) => { assert discr == 0; st.fields.len() }
        General(ref cases) => cases[discr as uint].fields.len()
    }
}

pub fn trans_GEP(bcx: block, r: &Repr, val: ValueRef, discr: int, ix: uint)
    -> ValueRef {
    // Note: if this ever needs to generate conditionals (e.g., if we
    // decide to do some kind of cdr-coding-like non-unique repr
    // someday), it'll need to return a possibly-new bcx as well.
    match *r {
        Unit(*) | CEnum(*) => {
            bcx.ccx().sess.bug(~"element access in C-like enum")
        }
        Univariant(ref st, dt) => {
            assert discr == 0;
            let val = match dt {
                NoDtor => val,
                DtorPresent | DtorAbsent => GEPi(bcx, val, [0, 0])
            };
            struct_GEP(bcx, st, val, ix, false)
        }
        General(ref cases) => {
            struct_GEP(bcx, &cases[discr as uint],
                       GEPi(bcx, val, [0, 1]), ix, true)
        }
    }
}

fn struct_GEP(bcx: block, st: &Struct, val: ValueRef, ix: uint,
              needs_cast: bool) -> ValueRef {
    let ccx = bcx.ccx();

    let val = if needs_cast {
        let real_llty = T_struct(st.fields.map(
            |&ty| type_of::type_of(ccx, ty)));
        PointerCast(bcx, val, T_ptr(real_llty))
    } else {
        val
    };

    GEPi(bcx, val, [0, ix])
}

pub fn trans_const(ccx: @CrateContext, r: &Repr, discr: int,
                   vals: &[ValueRef]) -> ValueRef {
    match *r {
        Unit(*) => {
            C_struct(~[])
        }
        CEnum(min, max) => {
            assert vals.len() == 0;
            assert min <= discr && discr <= max;
            C_int(ccx, discr)
        }
        Univariant(ref st, _dt) => {
            assert discr == 0;
            // consts are never destroyed, so the dtor flag is not needed
            C_struct(build_const_struct(ccx, st, vals))
        }
        General(ref cases) => {
            let case = &cases[discr as uint];
            let max_sz = cases.map(|s| s.size).max();
            let body = build_const_struct(ccx, case, vals);

            C_struct([C_int(ccx, discr),
                      C_packed_struct([C_struct(body)]),
                      padding(max_sz - case.size)])
        }
    }
}

fn padding(size: u64) -> ValueRef {
    C_undef(T_array(T_i8(), size /*bad*/as uint))
}

fn build_const_struct(ccx: @CrateContext, st: &Struct, vals: &[ValueRef])
    -> ~[ValueRef] {
    assert vals.len() == st.fields.len();

    let mut offset = 0;
    let mut cfields = ~[];
    for st.fields.eachi |i, &ty| {
        let llty = type_of::sizing_type_of(ccx, ty);
        let type_align = machine::llalign_of_min(ccx, llty)
            /*bad*/as u64;
        let val_align = machine::llalign_of_min(ccx, val_ty(vals[i]))
            /*bad*/as u64;
        let target_offset = roundup(offset, type_align);
        offset = roundup(offset, val_align);
        if (offset != target_offset) {
            cfields.push(padding(target_offset - offset));
            offset = target_offset;
        }
        assert !is_undef(vals[i]);
        // If that assert fails, could change it to wrap in a struct?
        cfields.push(vals[i]);
    }

    return cfields;
}

#[always_inline]
fn roundup(x: u64, a: u64) -> u64 { ((x + (a - 1)) / a) * a }

pub fn const_get_discrim(ccx: @CrateContext, r: &Repr, val: ValueRef)
    -> int {
    match *r {
        Unit(discr) => discr,
        CEnum(*) => const_to_int(val) as int,
        Univariant(*) => 0,
        General(*) => const_to_int(const_get_elt(ccx, val, [0])) as int,
    }
}

pub fn const_get_element(ccx: @CrateContext, r: &Repr, val: ValueRef,
                         _discr: int, ix: uint) -> ValueRef {
    // Not to be confused with common::const_get_elt.
    match *r {
        Unit(*) | CEnum(*) => ccx.sess.bug(~"element access in C-like enum \
                                             const"),
        Univariant(*) => const_struct_field(ccx, val, ix),
        General(*) => const_struct_field(ccx, const_get_elt(ccx, val,
                                                            [1, 0]), ix)
    }
}

fn const_struct_field(ccx: @CrateContext, val: ValueRef, ix: uint)
    -> ValueRef {
    // Get the ix-th non-undef element of the struct.
    let mut real_ix = 0; // actual position in the struct
    let mut ix = ix; // logical index relative to real_ix
    let mut field;
    loop {
        loop {
            field = const_get_elt(ccx, val, [real_ix]);
            if !is_undef(field) {
                break;
            }
            real_ix = real_ix + 1;
        }
        if ix == 0 {
            return field;
        }
        ix = ix - 1;
        real_ix = real_ix + 1;
    }
}
<commit_msg>Wrap const structs in as many LLVM structs as the non-const case.<commit_after>// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use core::libc::c_ulonglong;
use core::option::{Option, Some, None};
use core::vec;
use lib::llvm::{ValueRef, TypeRef, True, False};
use middle::trans::_match;
use middle::trans::build::*;
use middle::trans::common::*;
use middle::trans::machine;
use middle::trans::type_of;
use middle::ty;
use syntax::ast;
use util::ppaux::ty_to_str;

// XXX: should this be done with boxed traits instead of ML-style?
pub enum Repr {
    Unit(int),
    CEnum(int, int), /* discriminant range */
    Univariant(Struct, Destructor),
    General(~[Struct])
}

enum Destructor {
    DtorPresent,
    DtorAbsent,
    NoDtor
}

struct Struct {
    size: u64,
    align: u64,
    fields: ~[ty::t]
}

pub fn represent_node(bcx: block, node: ast::node_id)
    -> Repr {
    represent_type(bcx.ccx(), node_id_type(bcx, node))
}

pub fn represent_type(cx: @CrateContext, t: ty::t) -> Repr {
    debug!("Representing: %s", ty_to_str(cx.tcx, t));
    // XXX: cache this
    match ty::get(t).sty {
        ty::ty_tup(ref elems) => {
            Univariant(mk_struct(cx, *elems), NoDtor)
        }
        ty::ty_rec(ref fields) => {
            // XXX: Are these in the right order?
            Univariant(mk_struct(cx, fields.map(|f| f.mt.ty)), DtorAbsent)
        }
        ty::ty_struct(def_id, ref substs) => {
            let fields = ty::lookup_struct_fields(cx.tcx, def_id);
            let dt = ty::ty_dtor(cx.tcx, def_id).is_present();
            Univariant(mk_struct(cx, fields.map(|field| {
                ty::lookup_field_type(cx.tcx, def_id, field.id, substs)
            })), if dt { DtorPresent } else { DtorAbsent })
        }
        ty::ty_enum(def_id, ref substs) => {
            struct Case { discr: int, tys: ~[ty::t] };

            let cases = do ty::enum_variants(cx.tcx, def_id).map |vi| {
                let arg_tys = do vi.args.map |&raw_ty| {
                    ty::subst(cx.tcx, substs, raw_ty)
                };
                Case { discr: vi.disr_val, tys: arg_tys }
            };
            if cases.len() == 0 {
                // Uninhabitable; represent as unit
                Univariant(mk_struct(cx, ~[]), NoDtor)
            } else if cases.len() == 1 && cases[0].tys.len() == 0 {
                Unit(cases[0].discr)
            } else if cases.len() == 1 {
                // struct, tuple, newtype, etc.
                assert cases[0].discr == 0;
                Univariant(mk_struct(cx, cases[0].tys), NoDtor)
            } else if cases.all(|c| c.tys.len() == 0) {
                let discrs = cases.map(|c| c.discr);
                CEnum(discrs.min(), discrs.max())
            } else {
                if !cases.alli(|i,c| c.discr == (i as int)) {
                    cx.sess.bug(fmt!("non-C-like enum %s with specified \
                                      discriminants",
                                     ty::item_path_str(cx.tcx, def_id)))
                }
                General(cases.map(|c| mk_struct(cx, c.tys)))
            }
        }
        _ => cx.sess.bug(~"adt::represent_type called on non-ADT type")
    }
}

fn mk_struct(cx: @CrateContext, tys: &[ty::t]) -> Struct {
    let lltys = tys.map(|&ty| type_of::sizing_type_of(cx, ty));
    let llty_rec = T_struct(lltys);
    Struct {
        size: machine::llsize_of_alloc(cx, llty_rec) /*bad*/as u64,
        align: machine::llalign_of_min(cx, llty_rec) /*bad*/as u64,
        fields: vec::from_slice(tys)
    }
}

pub fn sizing_fields_of(cx: @CrateContext, r: &Repr) -> ~[TypeRef] {
    generic_fields_of(cx, r, true)
}
pub fn fields_of(cx: @CrateContext, r: &Repr) -> ~[TypeRef] {
    generic_fields_of(cx, r, false)
}
fn generic_fields_of(cx: @CrateContext, r: &Repr, sizing: bool)
    -> ~[TypeRef] {
    match *r {
        Unit(*) => ~[],
        CEnum(*) => ~[T_enum_discrim(cx)],
        Univariant(ref st, dt) => {
            let f = if sizing {
                st.fields.map(|&ty| type_of::sizing_type_of(cx, ty))
            } else {
                st.fields.map(|&ty| type_of::type_of(cx, ty))
            };
            match dt {
                NoDtor => f,
                DtorAbsent => ~[T_struct(f)],
                DtorPresent => ~[T_struct(f), T_i8()]
            }
        }
        General(ref sts) => {
            ~[T_enum_discrim(cx),
              T_array(T_i8(), sts.map(|st| st.size).max() /*bad*/as uint)]
        }
    }
}

fn load_discr(bcx: block, scrutinee: ValueRef, min: int, max: int)
    -> ValueRef {
    let ptr = GEPi(bcx, scrutinee, [0, 0]);
    if max + 1 == min {
        // i.e., if the range is everything.  The lo==hi case would be
        // rejected by the LLVM verifier (it would mean either an
        // empty set, which is impossible, or the entire range of the
        // type, which is pointless).
        Load(bcx, ptr)
    } else {
        // llvm::ConstantRange can deal with ranges that wrap around,
        // so an overflow on (max + 1) is fine.
        LoadRangeAssert(bcx, ptr, min as c_ulonglong,
                        (max + 1) as c_ulonglong,
                        /* signed: */ True)
    }
}

pub fn trans_switch(bcx: block, r: &Repr, scrutinee: ValueRef) ->
    (_match::branch_kind, Option<ValueRef>) {
    match *r {
        CEnum(min, max) => {
            (_match::switch, Some(load_discr(bcx, scrutinee, min, max)))
        }
        Unit(*) | Univariant(*) => {
            (_match::single, None)
        }
        General(ref cases) => {
            (_match::switch, Some(load_discr(bcx, scrutinee, 0,
                                             (cases.len() - 1) as int)))
        }
    }
}

pub fn trans_case(bcx: block, r: &Repr, discr: int) -> _match::opt_result {
    match *r {
        CEnum(*) => {
            _match::single_result(rslt(bcx, C_int(bcx.ccx(), discr)))
        }
        Unit(*) | Univariant(*)=> {
            bcx.ccx().sess.bug(~"no cases for univariants or structs")
        }
        General(*) => {
            _match::single_result(rslt(bcx, C_int(bcx.ccx(), discr)))
        }
    }
}

pub fn trans_set_discr(bcx: block, r: &Repr, val: ValueRef, discr: int) {
    match *r {
        Unit(the_discr) => {
            assert discr == the_discr;
        }
        CEnum(min, max) => {
            assert min <= discr && discr <= max;
            Store(bcx, C_int(bcx.ccx(), discr), GEPi(bcx, val, [0, 0]))
        }
        Univariant(_, DtorPresent) => {
            assert discr == 0;
            Store(bcx, C_u8(1), GEPi(bcx, val, [0, 1]))
        }
        Univariant(*) => {
            assert discr == 0;
        }
        General(*) => {
            Store(bcx, C_int(bcx.ccx(), discr), GEPi(bcx, val, [0, 0]))
        }
    }
}

pub fn num_args(r: &Repr, discr: int) -> uint {
    match *r {
        Unit(*) | CEnum(*) => 0,
        Univariant(ref st, _dt) => { assert discr == 0; st.fields.len() }
        General(ref cases) => cases[discr as uint].fields.len()
    }
}

pub fn trans_GEP(bcx: block, r: &Repr, val: ValueRef, discr: int, ix: uint)
    -> ValueRef {
    // Note: if this ever needs to generate conditionals (e.g., if we
    // decide to do some kind of cdr-coding-like non-unique repr
    // someday), it'll need to return a possibly-new bcx as well.
    match *r {
        Unit(*) | CEnum(*) => {
            bcx.ccx().sess.bug(~"element access in C-like enum")
        }
        Univariant(ref st, dt) => {
            assert discr == 0;
            let val = match dt {
                NoDtor => val,
                DtorPresent | DtorAbsent => GEPi(bcx, val, [0, 0])
            };
            struct_GEP(bcx, st, val, ix, false)
        }
        General(ref cases) => {
            struct_GEP(bcx, &cases[discr as uint],
                       GEPi(bcx, val, [0, 1]), ix, true)
        }
    }
}

fn struct_GEP(bcx: block, st: &Struct, val: ValueRef, ix: uint,
              needs_cast: bool) -> ValueRef {
    let ccx = bcx.ccx();

    let val = if needs_cast {
        let real_llty = T_struct(st.fields.map(
            |&ty| type_of::type_of(ccx, ty)));
        PointerCast(bcx, val, T_ptr(real_llty))
    } else {
        val
    };

    GEPi(bcx, val, [0, ix])
}

pub fn trans_const(ccx: @CrateContext, r: &Repr, discr: int,
                   vals: &[ValueRef]) -> ValueRef {
    match *r {
        Unit(*) => {
            C_struct(~[])
        }
        CEnum(min, max) => {
            assert vals.len() == 0;
            assert min <= discr && discr <= max;
            C_int(ccx, discr)
        }
        Univariant(ref st, dt) => {
            assert discr == 0;
            let s = C_struct(build_const_struct(ccx, st, vals));
            match dt {
                NoDtor => s,
                // The actual destructor flag doesn't need to be present.
                // But add an extra struct layer for compatibility.
                DtorPresent | DtorAbsent => C_struct(~[s])
            }
        }
        General(ref cases) => {
            let case = &cases[discr as uint];
            let max_sz = cases.map(|s| s.size).max();
            let body = build_const_struct(ccx, case, vals);

            C_struct([C_int(ccx, discr),
                      C_packed_struct([C_struct(body)]),
                      padding(max_sz - case.size)])
        }
    }
}

fn padding(size: u64) -> ValueRef {
    C_undef(T_array(T_i8(), size /*bad*/as uint))
}

fn build_const_struct(ccx: @CrateContext, st: &Struct, vals: &[ValueRef])
    -> ~[ValueRef] {
    assert vals.len() == st.fields.len();

    let mut offset = 0;
    let mut cfields = ~[];
    for st.fields.eachi |i, &ty| {
        let llty = type_of::sizing_type_of(ccx, ty);
        let type_align = machine::llalign_of_min(ccx, llty)
            /*bad*/as u64;
        let val_align = machine::llalign_of_min(ccx, val_ty(vals[i]))
            /*bad*/as u64;
        let target_offset = roundup(offset, type_align);
        offset = roundup(offset, val_align);
        if (offset != target_offset) {
            cfields.push(padding(target_offset - offset));
            offset = target_offset;
        }
        assert !is_undef(vals[i]);
        // If that assert fails, could change it to wrap in a struct?
        cfields.push(vals[i]);
    }

    return cfields;
}

#[always_inline]
fn roundup(x: u64, a: u64) -> u64 { ((x + (a - 1)) / a) * a }

pub fn const_get_discrim(ccx: @CrateContext, r: &Repr, val: ValueRef)
    -> int {
    match *r {
        Unit(discr) => discr,
        CEnum(*) => const_to_int(val) as int,
        Univariant(*) => 0,
        General(*) => const_to_int(const_get_elt(ccx, val, [0])) as int,
    }
}

pub fn const_get_element(ccx: @CrateContext, r: &Repr, val: ValueRef,
                         _discr: int, ix: uint) -> ValueRef {
    // Not to be confused with common::const_get_elt.
    match *r {
        Unit(*) | CEnum(*) => ccx.sess.bug(~"element access in C-like enum \
                                             const"),
        Univariant(_, NoDtor) => const_struct_field(ccx, val, ix),
        Univariant(*) => const_struct_field(ccx, const_get_elt(ccx, val,
                                                               [0]), ix),
        General(*) => const_struct_field(ccx, const_get_elt(ccx, val,
                                                            [1, 0]), ix)
    }
}

fn const_struct_field(ccx: @CrateContext, val: ValueRef, ix: uint)
    -> ValueRef {
    // Get the ix-th non-undef element of the struct.
    let mut real_ix = 0; // actual position in the struct
    let mut ix = ix; // logical index relative to real_ix
    let mut field;
    loop {
        loop {
            field = const_get_elt(ccx, val, [real_ix]);
            if !is_undef(field) {
                break;
            }
            real_ix = real_ix + 1;
        }
        if ix == 0 {
            return field;
        }
        ix = ix - 1;
        real_ix = real_ix + 1;
    }
}
<|endoftext|>