diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 58be69c8e88..a6b251402bc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,8 +15,8 @@ concurrency: env: CARGO_TERM_COLOR: always REGISTRY: ghcr.io - RUST_VERSION: 1.79.0 - NIGHTLY_RUST_VERSION: nightly-2024-07-16 + RUST_VERSION: 1.82.0 + NIGHTLY_RUST_VERSION: nightly-2024-10-21 jobs: get-fuel-core-version: @@ -412,7 +412,7 @@ jobs: with: cache-provider: "buildjet" - name: Cargo Run E2E Tests (EVM) - run: cargo run --locked --release --bin test -- --target evm --locked --no-encoding-v1 + run: cargo run --locked --release --bin test -- --target evm --locked --no-experimental new_encoding # TODO: Remove this upon merging std tests with the rest of the E2E tests. cargo-test-lib-std: diff --git a/Cargo.lock b/Cargo.lock index 4b067e99ad1..32887369fb4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2696,6 +2696,7 @@ dependencies = [ "serde_json", "sway-core", "sway-error", + "sway-features", "sway-ir", "sway-types", "sway-utils", @@ -2749,6 +2750,7 @@ dependencies = [ "serde", "serde_json", "sway-core", + "sway-features", "sway-types", "sway-utils", "tempfile", @@ -2809,6 +2811,7 @@ dependencies = [ "serde_json", "shellfish", "sway-core", + "sway-features", "sway-types", "thiserror", "tokio", @@ -2834,6 +2837,7 @@ dependencies = [ "serde_json", "sway-ast", "sway-core", + "sway-features", "sway-lsp", "sway-types", "swayfmt", @@ -2893,6 +2897,7 @@ dependencies = [ "serde_with", "sway-core", "sway-error", + "sway-features", "sway-types", "sway-utils", "sysinfo", @@ -2917,6 +2922,7 @@ dependencies = [ "rand", "rayon", "sway-core", + "sway-features", "sway-types", ] @@ -7622,6 +7628,7 @@ dependencies = [ "strum 0.26.3", "sway-ast", "sway-error", + "sway-features", "sway-ir", "sway-parse", "sway-types", @@ -7647,6 +7654,14 @@ dependencies = [ "uwuify", ] +[[package]] +name = "sway-features" +version = "0.66.2" +dependencies = [ + "clap 4.5.20", + "paste", +] + [[package]] name = "sway-ir" version = "0.66.2" @@ -7661,6 +7676,7 @@ dependencies = [ "prettydiff 0.7.0", "rustc-hash 1.1.0", "slotmap", + "sway-features", "sway-ir-macros", "sway-types", "sway-utils", @@ -7709,6 +7725,7 @@ dependencies = [ "sway-ast", "sway-core", "sway-error", + "sway-features", "sway-lsp-test-utils", "sway-parse", "sway-types", @@ -8117,6 +8134,7 @@ dependencies = [ "serde_json", "sway-core", "sway-error", + "sway-features", "sway-ir", "sway-types", "sway-utils", diff --git a/Cargo.toml b/Cargo.toml index 760055fc845..21e63609454 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,6 +17,7 @@ members = [ "sway-ast", "sway-core", "sway-error", + "sway-features", "sway-ir", "sway-ir/sway-ir-macros", "sway-lsp", @@ -60,6 +61,7 @@ forc-tx = { path = "forc-plugins/forc-tx/", version = "0.66.2" } sway-ast = { path = "sway-ast/", version = "0.66.2" } sway-core = { path = "sway-core/", version = "0.66.2" } sway-error = { path = "sway-error/", version = "0.66.2" } +sway-features = { path = "sway-features/", version = "0.66.2" } sway-lsp = { path = "sway-lsp/", version = "0.66.2" } sway-parse = { path = "sway-parse/", version = "0.66.2" } sway-types = { path = "sway-types/", version = "0.66.2" } @@ -164,6 +166,7 @@ libtest-mimic = "0.7" lsp-types = "0.94" mdbook = { version = "0.4", default-features = false } minifier = "0.3" +normalize-path = "0.2.1" notify = "6.1" notify-debouncer-mini = "0.4" num-bigint = "0.4" diff --git a/docs/book/src/blockchain-development/native_assets.md b/docs/book/src/blockchain-development/native_assets.md index bc589ccda85..7f1298326f9 100644 --- a/docs/book/src/blockchain-development/native_assets.md +++ b/docs/book/src/blockchain-development/native_assets.md @@ -223,6 +223,7 @@ use std::{ constants::DEFAULT_SUB_ID, context::msg_amount, string::String, + contract_id::ContractId }; configurable { @@ -408,7 +409,8 @@ use std::{ }, context::this_balance, storage::storage_string::*, - string::String + string::String, + contract_id::ContractId }; storage { diff --git a/forc-pkg/Cargo.toml b/forc-pkg/Cargo.toml index 7c5618d4a4e..7b1bc68587b 100644 --- a/forc-pkg/Cargo.toml +++ b/forc-pkg/Cargo.toml @@ -17,10 +17,7 @@ forc-tracing.workspace = true forc-util.workspace = true fuel-abi-types.workspace = true futures.workspace = true -git2 = { workspace = true, features = [ - "vendored-libgit2", - "vendored-openssl", -] } +git2 = { workspace = true, features = ["vendored-libgit2", "vendored-openssl"] } gix-url = { workspace = true, features = ["serde"] } hex.workspace = true ipfs-api-backend-hyper = { workspace = true, features = ["with-builder"] } @@ -33,6 +30,7 @@ serde_json.workspace = true serde_with.workspace = true sway-core.workspace = true sway-error.workspace = true +sway-features.workspace = true sway-types.workspace = true sway-utils.workspace = true tar.workspace = true diff --git a/forc-pkg/src/manifest/build_profile.rs b/forc-pkg/src/manifest/build_profile.rs index 59610ee8bf9..af5822ec693 100644 --- a/forc-pkg/src/manifest/build_profile.rs +++ b/forc-pkg/src/manifest/build_profile.rs @@ -1,12 +1,6 @@ use serde::{Deserialize, Serialize}; use sway_core::{OptLevel, PrintAsm, PrintIr}; -#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Default)] -#[serde(rename_all = "kebab-case")] -pub struct ExperimentalFlags { - pub new_encoding: bool, -} - /// Parameters to pass through to the `sway_core::BuildConfig` during compilation. #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] #[serde(rename_all = "kebab-case")] @@ -39,8 +33,6 @@ pub struct BuildProfile { pub reverse_results: bool, #[serde(default)] pub optimization_level: OptLevel, - #[serde(default)] - pub experimental: ExperimentalFlags, } impl BuildProfile { @@ -65,9 +57,6 @@ impl BuildProfile { error_on_warnings: false, reverse_results: false, optimization_level: OptLevel::Opt0, - experimental: ExperimentalFlags { - new_encoding: false, - }, } } @@ -88,9 +77,6 @@ impl BuildProfile { error_on_warnings: false, reverse_results: false, optimization_level: OptLevel::Opt1, - experimental: ExperimentalFlags { - new_encoding: false, - }, } } } @@ -103,10 +89,9 @@ impl Default for BuildProfile { #[cfg(test)] mod tests { + use crate::{BuildProfile, PackageManifest}; use sway_core::{OptLevel, PrintAsm, PrintIr}; - use crate::{manifest::build_profile::ExperimentalFlags, BuildProfile, PackageManifest}; - #[test] fn test_build_profiles() { let manifest = PackageManifest::from_dir("./tests/sections").expect("manifest"); @@ -160,7 +145,6 @@ mod tests { error_on_warnings: true, reverse_results: true, optimization_level: OptLevel::Opt0, - experimental: ExperimentalFlags { new_encoding: true }, }; let profile = build_profiles.get("release").expect("release profile"); assert_eq!(*profile, expected); diff --git a/forc-pkg/src/manifest/mod.rs b/forc-pkg/src/manifest/mod.rs index 28729aff757..5ce50d65804 100644 --- a/forc-pkg/src/manifest/mod.rs +++ b/forc-pkg/src/manifest/mod.rs @@ -200,6 +200,8 @@ pub struct Project { pub entry: String, pub implicit_std: Option, pub forc_version: Option, + #[serde(default)] + pub experimental: HashMap, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq)] @@ -771,7 +773,7 @@ impl std::ops::Deref for PackageManifestFile { /// This can be configured using environment variables: /// - use `FORC_IMPLICIT_STD_PATH` for the path for the std-lib; /// - use `FORC_IMPLICIT_STD_GIT`, `FORC_IMPLICIT_STD_GIT_TAG` and/or `FORC_IMPLICIT_STD_GIT_BRANCH` to configure -/// the git repo of the std-lib. +/// the git repo of the std-lib. fn implicit_std_dep() -> Dependency { if let Ok(path) = std::env::var("FORC_IMPLICIT_STD_PATH") { return Dependency::Detailed(DependencyDetails { diff --git a/forc-pkg/src/pkg.rs b/forc-pkg/src/pkg.rs index 26056950242..50d8b24c703 100644 --- a/forc-pkg/src/pkg.rs +++ b/forc-pkg/src/pkg.rs @@ -1,10 +1,7 @@ use crate::manifest::GenericManifestFile; use crate::{ lock::Lock, - manifest::{ - build_profile::ExperimentalFlags, Dependency, ManifestFile, MemberManifestFiles, - PackageManifestFile, - }, + manifest::{Dependency, ManifestFile, MemberManifestFiles, PackageManifestFile}, source::{self, IPFSNode, Source}, BuildProfile, }; @@ -51,6 +48,7 @@ use sway_core::{ }; use sway_core::{set_bytecode_configurables_offset, PrintAsm, PrintIr}; use sway_error::{error::CompileError, handler::Handler, warning::CompileWarning}; +use sway_features::ExperimentalFeatures; use sway_types::constants::{CORE, PRELUDE, STD}; use sway_types::{Ident, Span, Spanned}; use sway_utils::{constants, time_expr, PerformanceData, PerformanceMetric}; @@ -310,8 +308,10 @@ pub struct BuildOpts { pub tests: bool, /// The set of options to filter by member project kind. pub member_filter: MemberFilter, - /// Set of experimental flags - pub experimental: ExperimentalFlags, + /// Set of enabled experimental flags + pub experimental: Vec, + /// Set of disabled experimental flags + pub no_experimental: Vec, } /// The set of options to filter type of projects to build in a workspace. @@ -893,22 +893,19 @@ fn validate_version(member_manifests: &MemberManifestFiles) -> Result<()> { /// If required minimum forc version is higher than current forc version return an error with /// upgrade instructions fn validate_pkg_version(pkg_manifest: &PackageManifestFile) -> Result<()> { - match &pkg_manifest.project.forc_version { - Some(min_forc_version) => { - // Get the current version of the toolchain - let crate_version = env!("CARGO_PKG_VERSION"); - let toolchain_version = semver::Version::parse(crate_version)?; - if toolchain_version < *min_forc_version { - bail!( - "{:?} requires forc version {} but current forc version is {}\nUpdate the toolchain by following: https://fuellabs.github.io/sway/v{}/introduction/installation.html", - pkg_manifest.project.name, - min_forc_version, - crate_version, - crate_version - ); - } + if let Some(min_forc_version) = &pkg_manifest.project.forc_version { + // Get the current version of the toolchain + let crate_version = env!("CARGO_PKG_VERSION"); + let toolchain_version = semver::Version::parse(crate_version)?; + if toolchain_version < *min_forc_version { + bail!( + "{:?} requires forc version {} but current forc version is {}\nUpdate the toolchain by following: https://fuellabs.github.io/sway/v{}/introduction/installation.html", + pkg_manifest.project.name, + min_forc_version, + crate_version, + crate_version + ); } - None => {} }; Ok(()) } @@ -1565,10 +1562,7 @@ pub fn sway_build_config( .with_include_tests(build_profile.include_tests) .with_time_phases(build_profile.time_phases) .with_metrics(build_profile.metrics_outfile.clone()) - .with_optimization_level(build_profile.optimization_level) - .with_experimental(sway_core::ExperimentalFlags { - new_encoding: build_profile.experimental.new_encoding, - }); + .with_optimization_level(build_profile.optimization_level); Ok(build_config) } @@ -1594,7 +1588,7 @@ pub fn dependency_namespace( node: NodeIx, engines: &Engines, contract_id_value: Option, - experimental: sway_core::ExperimentalFlags, + experimental: ExperimentalFeatures, ) -> Result> { // TODO: Clean this up when config-time constants v1 are removed. let node_idx = &graph[node]; @@ -1760,6 +1754,7 @@ pub fn compile( engines: &Engines, namespace: &mut namespace::Root, source_map: &mut SourceMap, + experimental: ExperimentalFeatures, ) -> Result { let mut metrics = PerformanceData::default(); @@ -1795,6 +1790,7 @@ pub fn compile( Some(&sway_build_config), &pkg.name, None, + experimental ), Some(sway_build_config.clone()), metrics @@ -1825,13 +1821,19 @@ pub fn compile( let asm_res = time_expr!( "compile ast to asm", "compile_ast_to_asm", - sway_core::ast_to_asm(&handler, engines, &programs, &sway_build_config), + sway_core::ast_to_asm( + &handler, + engines, + &programs, + &sway_build_config, + experimental + ), Some(sway_build_config.clone()), metrics ); - const OLD_ENCODING_VERSION: &str = "0"; - const NEW_ENCODING_VERSION: &str = "1"; + const ENCODING_V0: &str = "0"; + const ENCODING_V1: &str = "1"; const SPEC_VERSION: &str = "1"; let mut program_abi = match pkg.target { @@ -1847,11 +1849,11 @@ pub fn compile( type_ids_to_full_type_str: HashMap::::new(), }, engines, - profile - .experimental - .new_encoding - .then(|| NEW_ENCODING_VERSION.into()) - .unwrap_or(OLD_ENCODING_VERSION.into()), + if experimental.new_encoding { + ENCODING_V1.into() + } else { + ENCODING_V0.into() + }, SPEC_VERSION.into(), ), Some(sway_build_config.clone()), @@ -2078,7 +2080,6 @@ fn build_profile_from_opts( metrics_outfile, tests, error_on_warnings, - experimental, .. } = build_options; @@ -2119,9 +2120,7 @@ fn build_profile_from_opts( } profile.include_tests |= tests; profile.error_on_warnings |= error_on_warnings; - profile.experimental = ExperimentalFlags { - new_encoding: experimental.new_encoding, - }; + // profile.experimental = *experimental; Ok(profile) } @@ -2160,6 +2159,7 @@ pub fn build_with_options(build_options: &BuildOpts) -> Result { build_target, member_filter, experimental, + no_experimental, .. } = &build_options; @@ -2205,9 +2205,8 @@ pub fn build_with_options(build_options: &BuildOpts) -> Result { *build_target, &build_profile, &outputs, - sway_core::ExperimentalFlags { - new_encoding: experimental.new_encoding, - }, + experimental, + no_experimental, )?; let output_dir = pkg.output_directory.as_ref().map(PathBuf::from); let total_size = built_packages @@ -2318,7 +2317,8 @@ pub fn build( target: BuildTarget, profile: &BuildProfile, outputs: &HashSet, - experimental: sway_core::ExperimentalFlags, + experimental: &[sway_features::Feature], + no_experimental: &[sway_features::Feature], ) -> anyhow::Result> { let mut built_packages = Vec::new(); @@ -2353,6 +2353,13 @@ pub fn build( &pkg.source.display_compiling(manifest.dir()), ); + let experimental = ExperimentalFeatures::new( + &manifest.project.experimental, + experimental, + no_experimental, + ) + .map_err(|err| anyhow!("{err}"))?; + let descriptor = PackageDescriptor { name: pkg.name.clone(), target, @@ -2410,6 +2417,7 @@ pub fn build( &engines, &mut dep_namespace, &mut source_map, + experimental, )?; if let Some(outfile) = profile.metrics_outfile { @@ -2483,6 +2491,7 @@ pub fn build( &engines, &mut dep_namespace, &mut source_map, + experimental, )?; if let Some(outfile) = profile.metrics_outfile { @@ -2528,7 +2537,8 @@ pub fn check( include_tests: bool, engines: &Engines, retrigger_compilation: Option>, - experimental: sway_core::ExperimentalFlags, + experimental: &[sway_features::Feature], + no_experimental: &[sway_features::Feature], ) -> anyhow::Result, Handler)>> { let mut lib_namespace_map = HashMap::default(); let mut source_map = SourceMap::new(); @@ -2540,6 +2550,13 @@ pub fn check( let pkg = &plan.graph[node]; let manifest = &plan.manifest_map()[&pkg.id()]; + let experimental = ExperimentalFeatures::new( + &manifest.project.experimental, + experimental, + no_experimental, + ) + .map_err(|err| anyhow!("{err}"))?; + // This is necessary because `CONTRACT_ID` is a special constant that's injected into the // compiler's namespace. Although we only know the contract id during building, we are // inserting a dummy value here to avoid false error signals being reported in LSP. @@ -2587,6 +2604,7 @@ pub fn check( Some(&build_config), &pkg.name, retrigger_compilation.clone(), + experimental, ); if retrigger_compilation diff --git a/forc-pkg/tests/sections/Forc.toml b/forc-pkg/tests/sections/Forc.toml index 88b012cd138..4c65994f702 100644 --- a/forc-pkg/tests/sections/Forc.toml +++ b/forc-pkg/tests/sections/Forc.toml @@ -10,7 +10,7 @@ name = "sections" print-ast = true print-dca-graph = "dca_graph" print-dca-graph-url-format = "print_dca_graph_url_format" -print-ir = { initial = false, final = true, modified = true, passes = []} +print-ir = { initial = false, final = true, modified = true, passes = [] } print-asm = { virtual = true, allocated = true, final = true } print-bytecode = true terse = true @@ -20,10 +20,13 @@ include-tests = true error-on-warnings = true reverse-results = true optimization-level = 0 -experimental = { new-encoding = true } +experimental = { encoding-v1 = true } [build-profile.custom_asm] print-asm = { virtual = false, allocated = false, final = true } [build-profile.custom_ir] -print-ir = { initial = true, final = false, modified = true, passes = ["dce", "sroa"]} +print-ir = { initial = true, final = false, modified = true, passes = [ + "dce", + "sroa", +] } diff --git a/forc-plugins/forc-client/Cargo.toml b/forc-plugins/forc-client/Cargo.toml index ee1b03ea173..c3c5d5d57da 100644 --- a/forc-plugins/forc-client/Cargo.toml +++ b/forc-plugins/forc-client/Cargo.toml @@ -40,9 +40,14 @@ rpassword.workspace = true serde.workspace = true serde_json.workspace = true sway-core.workspace = true +sway-features.workspace = true sway-types.workspace = true sway-utils.workspace = true -tokio = { workspace = true, features = ["macros", "process", "rt-multi-thread"] } +tokio = { workspace = true, features = [ + "macros", + "process", + "rt-multi-thread", +] } toml_edit.workspace = true tracing.workspace = true diff --git a/forc-plugins/forc-client/src/cmd/deploy.rs b/forc-plugins/forc-client/src/cmd/deploy.rs index 87428dfbca7..a30a40b3b0b 100644 --- a/forc-plugins/forc-client/src/cmd/deploy.rs +++ b/forc-plugins/forc-client/src/cmd/deploy.rs @@ -84,9 +84,8 @@ pub struct Command { #[clap(long, verbatim_doc_comment, name = "JSON_FILE_PATH")] pub override_storage_slots: Option, - /// Disable the "new encoding" feature - #[clap(long)] - pub no_encoding_v1: bool, + #[clap(flatten)] + pub experimental: sway_features::CliFields, /// AWS KMS signer arn. If present forc-deploy will automatically use AWS KMS signer instead of forc-wallet. #[clap(long)] diff --git a/forc-plugins/forc-client/src/cmd/run.rs b/forc-plugins/forc-client/src/cmd/run.rs index 5875b270adc..85b5ec31fe9 100644 --- a/forc-plugins/forc-client/src/cmd/run.rs +++ b/forc-plugins/forc-client/src/cmd/run.rs @@ -56,7 +56,6 @@ pub struct Command { #[clap(long)] pub args: Option>, - /// Disable the "new encoding" feature - #[clap(long)] - pub no_encoding_v1: bool, + #[clap(flatten)] + pub experimental: sway_features::CliFields, } diff --git a/forc-plugins/forc-client/src/op/deploy.rs b/forc-plugins/forc-client/src/op/deploy.rs index cdc5d35162c..ace89c3ddae 100644 --- a/forc-plugins/forc-client/src/op/deploy.rs +++ b/forc-plugins/forc-client/src/op/deploy.rs @@ -38,7 +38,7 @@ use fuels::{ use fuels_accounts::{provider::Provider, Account, ViewOnlyAccount}; use fuels_core::types::{transaction::TxPolicies, transaction_builders::CreateTransactionBuilder}; use futures::FutureExt; -use pkg::{manifest::build_profile::ExperimentalFlags, BuildProfile, BuiltPackage}; +use pkg::{BuildProfile, BuiltPackage}; use serde::{Deserialize, Serialize}; use std::{ collections::BTreeMap, @@ -47,8 +47,7 @@ use std::{ sync::Arc, time::Duration, }; -use sway_core::BuildTarget; -use sway_core::{asm_generation::ProgramABI, language::parsed::TreeType}; +use sway_core::{asm_generation::ProgramABI, language::parsed::TreeType, BuildTarget}; /// Default maximum contract size allowed for a single contract. If the target /// contract size is bigger than this amount, forc-deploy will automatically @@ -927,9 +926,8 @@ fn build_opts_from_cmd(cmd: &cmd::Deploy, member_filter: pkg::MemberFilter) -> p build_target: BuildTarget::default(), tests: false, member_filter, - experimental: ExperimentalFlags { - new_encoding: !cmd.no_encoding_v1, - }, + experimental: cmd.experimental.experimental.clone(), + no_experimental: cmd.experimental.no_experimental.clone(), } } diff --git a/forc-plugins/forc-client/src/op/run/mod.rs b/forc-plugins/forc-client/src/op/run/mod.rs index 5e58835fca0..debf0579c19 100644 --- a/forc-plugins/forc-client/src/op/run/mod.rs +++ b/forc-plugins/forc-client/src/op/run/mod.rs @@ -23,7 +23,7 @@ use fuels::{ }, }; use fuels_accounts::{provider::Provider, Account}; -use pkg::{manifest::build_profile::ExperimentalFlags, BuiltPackage}; +use pkg::BuiltPackage; use std::time::Duration; use std::{path::PathBuf, str::FromStr}; use sway_core::language::parsed::TreeType; @@ -272,8 +272,7 @@ fn build_opts_from_cmd(cmd: &cmd::Run) -> pkg::BuildOpts { debug_outfile: cmd.build_output.debug_file.clone(), tests: false, member_filter: pkg::MemberFilter::only_scripts(), - experimental: ExperimentalFlags { - new_encoding: !cmd.no_encoding_v1, - }, + experimental: cmd.experimental.experimental.clone(), + no_experimental: cmd.experimental.no_experimental.clone(), } } diff --git a/forc-plugins/forc-debug/Cargo.toml b/forc-plugins/forc-debug/Cargo.toml index 4911d214c2e..84c3d05797d 100644 --- a/forc-plugins/forc-debug/Cargo.toml +++ b/forc-plugins/forc-debug/Cargo.toml @@ -23,6 +23,7 @@ serde.workspace = true serde_json.workspace = true shellfish = { workspace = true, features = ["async", "rustyline", "tokio"] } sway-core.workspace = true +sway-features.workspace = true sway-types.workspace = true thiserror.workspace = true tokio = { workspace = true, features = [ diff --git a/forc-plugins/forc-debug/src/server/handlers/handle_launch.rs b/forc-plugins/forc-debug/src/server/handlers/handle_launch.rs index fd281261347..3ae3c2f3539 100644 --- a/forc-plugins/forc-debug/src/server/handlers/handle_launch.rs +++ b/forc-plugins/forc-debug/src/server/handlers/handle_launch.rs @@ -54,10 +54,6 @@ impl DapServer { } } - let experimental = sway_core::ExperimentalFlags { - new_encoding: false, - }; - // 1. Build the packages let manifest_file = forc_pkg::manifest::ManifestFile::from_dir(&self.state.program_path) .map_err(|err| AdapterError::BuildFailed { @@ -110,7 +106,8 @@ impl DapServer { ..Default::default() }, &outputs, - experimental, + &[], + &[], ) .map_err(|err| AdapterError::BuildFailed { reason: format!("build packages: {:?}", err), diff --git a/forc-plugins/forc-doc/Cargo.toml b/forc-plugins/forc-doc/Cargo.toml index e6780e0fe80..ebfd2bc7197 100644 --- a/forc-plugins/forc-doc/Cargo.toml +++ b/forc-plugins/forc-doc/Cargo.toml @@ -23,6 +23,7 @@ serde.workspace = true serde_json.workspace = true sway-ast.workspace = true sway-core.workspace = true +sway-features.workspace = true sway-lsp.workspace = true sway-types.workspace = true swayfmt.workspace = true diff --git a/forc-plugins/forc-doc/src/cli.rs b/forc-plugins/forc-doc/src/cli.rs index 8ba9906203f..309bfb880d6 100644 --- a/forc-plugins/forc-doc/src/cli.rs +++ b/forc-plugins/forc-doc/src/cli.rs @@ -53,7 +53,6 @@ pub struct Command { #[cfg(test)] pub(crate) doc_path: Option, - /// Disable the "new encoding" feature - #[clap(long)] - pub no_encoding_v1: bool, + #[clap(flatten)] + pub experimental: sway_features::CliFields, } diff --git a/forc-plugins/forc-doc/src/lib.rs b/forc-plugins/forc-doc/src/lib.rs index d629b606563..4043cfde1f2 100644 --- a/forc-plugins/forc-doc/src/lib.rs +++ b/forc-plugins/forc-doc/src/lib.rs @@ -55,7 +55,6 @@ pub struct ProgramInfo<'a> { pub fn compile_html( build_instructions: &Command, get_doc_dir: &dyn Fn(&Command) -> String, - experimental: sway_core::ExperimentalFlags, ) -> Result<(PathBuf, Box)> { // get manifest directory let dir = if let Some(ref path) = build_instructions.manifest_path { @@ -108,7 +107,8 @@ pub fn compile_html( tests_enabled, &engines, None, - experimental, + &build_instructions.experimental.experimental, + &build_instructions.experimental.no_experimental, )?; let raw_docs = if build_instructions.no_deps { diff --git a/forc-plugins/forc-doc/src/main.rs b/forc-plugins/forc-doc/src/main.rs index ab990727d7f..5b3082fd1a7 100644 --- a/forc-plugins/forc-doc/src/main.rs +++ b/forc-plugins/forc-doc/src/main.rs @@ -12,13 +12,7 @@ use std::{ pub fn main() -> Result<()> { let build_instructions = Command::parse(); - let (doc_path, pkg_manifest) = compile_html( - &build_instructions, - &get_doc_dir, - sway_core::ExperimentalFlags { - new_encoding: !build_instructions.no_encoding_v1, - }, - )?; + let (doc_path, pkg_manifest) = compile_html(&build_instructions, &get_doc_dir)?; // CSS, icons and logos static ASSETS_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/src/static.files"); diff --git a/forc-plugins/forc-doc/src/tests/expects/impl_trait/mod.rs b/forc-plugins/forc-doc/src/tests/expects/impl_trait/mod.rs index 8f96a41e856..9ead7b2f1dc 100644 --- a/forc-plugins/forc-doc/src/tests/expects/impl_trait/mod.rs +++ b/forc-plugins/forc-doc/src/tests/expects/impl_trait/mod.rs @@ -10,7 +10,6 @@ use std::{ collections::HashSet, path::{Path, PathBuf}, }; -use sway_core::ExperimentalFlags; /// The path to the generated HTML of the type the traits are implemented on. const IMPL_FOR: &str = "bar/struct.Bar.html"; @@ -26,19 +25,12 @@ fn test_impl_traits_default() { doc_path: Some(doc_dir_name.into()), ..Default::default() }; - let (doc_path, _) = compile_html( - &command, - &get_doc_dir, - ExperimentalFlags { - new_encoding: false, - }, - ) - .unwrap(); + let (doc_path, _) = compile_html(&command, &get_doc_dir).unwrap(); assert_index_html( &doc_path, project_name, &expect![[r##" - Bar in bar - Sway
pub struct Bar {}

Implementations

fn foo_bar()

Trait Implementations

fn foo()

something more about foo();

+ Bar in bar - Sway
pub struct Bar {}

Implementations

fn foo_bar()

Trait Implementations

fn abi_encode(self, buffer: Buffer) -> Buffer

fn abi_decode(refmut _buffer: BufferReader) -> Self

fn foo()

something more about foo();

fn add(self, other: Self) -> Self

fn subtract(self, other: Self) -> Self

"##]], ); assert_search_js( @@ -183,21 +175,13 @@ fn test_impl_traits_no_deps() { no_deps: true, ..Default::default() }; - let (doc_path, _) = compile_html( - &command, - &get_doc_dir, - ExperimentalFlags { - new_encoding: false, - }, - ) - .unwrap(); + let (doc_path, _) = compile_html(&command, &get_doc_dir).unwrap(); assert_index_html( &doc_path, project_name, - &expect![[ - r##"Bar in bar - Sway
pub struct Bar {}

Implementations

fn foo_bar()

Trait Implementations

fn foo()

something more about foo();

-

fn add(self, other: Self) -> Self

fn subtract(self, other: Self) -> Self

"## - ]], + &expect![[r##" + Bar in bar - Sway
pub struct Bar {}

Implementations

fn foo_bar()

Trait Implementations

fn abi_encode(self, buffer: Buffer) -> Buffer

fn abi_decode(refmut _buffer: BufferReader) -> Self

fn foo()

something more about foo();

+

fn add(self, other: Self) -> Self

fn subtract(self, other: Self) -> Self

"##]], ); assert_search_js( &doc_path, diff --git a/forc-plugins/forc-doc/tests/lib.rs b/forc-plugins/forc-doc/tests/lib.rs index 158a6c2613e..9624b656a4f 100644 --- a/forc-plugins/forc-doc/tests/lib.rs +++ b/forc-plugins/forc-doc/tests/lib.rs @@ -9,12 +9,6 @@ fn builds_lib_std_docs() { ..Default::default() }; println!("Building docs for {:?}", build_instructions.manifest_path); - let res = compile_html( - &build_instructions, - &get_doc_dir, - sway_core::ExperimentalFlags { - new_encoding: !build_instructions.no_encoding_v1, - }, - ); + let res = compile_html(&build_instructions, &get_doc_dir); assert!(res.is_ok()); } diff --git a/forc-test/Cargo.toml b/forc-test/Cargo.toml index 3034b60dfbf..1a012d6fdc1 100644 --- a/forc-test/Cargo.toml +++ b/forc-test/Cargo.toml @@ -18,4 +18,5 @@ fuels-core.workspace = true rand.workspace = true rayon.workspace = true sway-core.workspace = true +sway-features.workspace = true sway-types.workspace = true diff --git a/forc-test/src/lib.rs b/forc-test/src/lib.rs index 58394055d75..c7bd19e95fd 100644 --- a/forc-test/src/lib.rs +++ b/forc-test/src/lib.rs @@ -12,7 +12,6 @@ use fuel_vm::checked_transaction::builder::TransactionBuilderExt; use fuel_vm::{self as vm}; use fuels_core::codec::ABIDecoder; use fuels_core::types::param_types::ParamType; -use pkg::manifest::build_profile::ExperimentalFlags; use pkg::TestPassCondition; use pkg::{Built, BuiltPackage}; use rand::{Rng, SeedableRng}; @@ -154,8 +153,10 @@ pub struct TestOpts { pub time_phases: bool, /// Output compilation metrics into file. pub metrics_outfile: Option, - /// Set of experimental flags - pub experimental: ExperimentalFlags, + /// Set of enabled experimental flags + pub experimental: Vec, + /// Set of disabled experimental flags + pub no_experimental: Vec, } /// The set of options provided for controlling logs printed for each test. @@ -456,6 +457,7 @@ impl From for pkg::BuildOpts { tests: true, member_filter: Default::default(), experimental: val.experimental, + no_experimental: val.no_experimental, } } } @@ -478,6 +480,7 @@ impl TestOpts { tests: true, member_filter: Default::default(), experimental: self.experimental, + no_experimental: self.no_experimental, } } } diff --git a/forc/Cargo.toml b/forc/Cargo.toml index 2f5d6dda481..578e427cee8 100644 --- a/forc/Cargo.toml +++ b/forc/Cargo.toml @@ -34,6 +34,7 @@ serde = { workspace = true, features = ["derive"] } serde_json.workspace = true sway-core.workspace = true sway-error.workspace = true +sway-features.workspace = true sway-ir.workspace = true sway-types.workspace = true sway-utils.workspace = true diff --git a/forc/src/cli/commands/build.rs b/forc/src/cli/commands/build.rs index ae88dfebfdc..c83242d43df 100644 --- a/forc/src/cli/commands/build.rs +++ b/forc/src/cli/commands/build.rs @@ -17,13 +17,13 @@ forc_util::cli_examples! { /// - `script`, `predicate` and `contract` projects will produce their bytecode in binary format `.bin`. /// /// - `script` projects will also produce a file containing the hash of the bytecode binary -/// `-bin-hash` (using `fuel_cypto::Hasher`). +/// `-bin-hash` (using `fuel_cypto::Hasher`). /// /// - `predicate` projects will also produce a file containing the **root** hash of the bytecode binary -/// `-bin-root` (using `fuel_tx::Contract::root_from_code`). +/// `-bin-root` (using `fuel_tx::Contract::root_from_code`). /// /// - `contract` and `library` projects will also produce the public ABI in JSON format -/// `-abi.json`. +/// `-abi.json`. #[derive(Debug, Default, Parser)] #[clap(bin_name = "forc build", version, after_help = help())] pub struct Command { @@ -33,9 +33,8 @@ pub struct Command { #[clap(long)] pub tests: bool, - /// Disable the "new encoding" feature - #[clap(long)] - pub no_encoding_v1: bool, + #[clap(flatten)] + pub experimental: sway_features::CliFields, } pub(crate) fn exec(command: Command) -> ForcResult<()> { diff --git a/forc/src/cli/commands/check.rs b/forc/src/cli/commands/check.rs index 4d5f41e76a3..3a3406ac9e8 100644 --- a/forc/src/cli/commands/check.rs +++ b/forc/src/cli/commands/check.rs @@ -45,9 +45,8 @@ pub struct Command { #[clap(long)] pub ipfs_node: Option, - /// Disable the "new encoding" feature - #[clap(long)] - pub no_encoding_v1: bool, + #[clap(flatten)] + pub experimental: sway_features::CliFields, } pub(crate) fn exec(command: Command) -> ForcResult<()> { diff --git a/forc/src/cli/commands/contract_id.rs b/forc/src/cli/commands/contract_id.rs index 55848428aa8..85640937943 100644 --- a/forc/src/cli/commands/contract_id.rs +++ b/forc/src/cli/commands/contract_id.rs @@ -29,9 +29,8 @@ pub struct Command { #[clap(flatten)] pub salt: Salt, - /// Disable the "new encoding" feature - #[clap(long)] - pub no_encoding_v1: bool, + #[clap(flatten)] + pub experimental: sway_features::CliFields, } pub(crate) fn exec(cmd: Command) -> ForcResult<()> { diff --git a/forc/src/cli/commands/predicate_root.rs b/forc/src/cli/commands/predicate_root.rs index 5c9cbb6bf04..676c85e998f 100644 --- a/forc/src/cli/commands/predicate_root.rs +++ b/forc/src/cli/commands/predicate_root.rs @@ -26,9 +26,8 @@ pub struct Command { #[clap(flatten)] pub build_profile: BuildProfile, - /// Disable the "new encoding" feature - #[clap(long)] - pub no_encoding_v1: bool, + #[clap(flatten)] + pub experimental: sway_features::CliFields, } pub(crate) fn exec(cmd: Command) -> ForcResult<()> { diff --git a/forc/src/cli/commands/test.rs b/forc/src/cli/commands/test.rs index 5ffa1c51d75..d556af6b790 100644 --- a/forc/src/cli/commands/test.rs +++ b/forc/src/cli/commands/test.rs @@ -5,7 +5,6 @@ use forc_pkg as pkg; use forc_test::{decode_log_data, TestFilter, TestRunnerCount, TestedPackage}; use forc_tracing::println_action_green; use forc_util::{tx_utils::format_log_receipts, ForcError, ForcResult}; -use pkg::manifest::build_profile::ExperimentalFlags; use sway_core::fuel_prelude::fuel_tx::Receipt; use tracing::info; @@ -52,9 +51,8 @@ pub struct Command { /// threads available in your system. pub test_threads: Option, - /// Disable the "new encoding" feature - #[clap(long)] - pub no_encoding_v1: bool, + #[clap(flatten)] + pub experimental: sway_features::CliFields, } /// The set of options provided for controlling output of a test. @@ -256,9 +254,8 @@ fn opts_from_cmd(cmd: Command) -> forc_test::TestOpts { binary_outfile: cmd.build.output.bin_file, debug_outfile: cmd.build.output.debug_file, build_target: cmd.build.build_target, - experimental: ExperimentalFlags { - new_encoding: !cmd.no_encoding_v1, - }, + experimental: cmd.experimental.experimental, + no_experimental: cmd.experimental.no_experimental, } } diff --git a/forc/src/ops/forc_build.rs b/forc/src/ops/forc_build.rs index 49c0c27f955..1118bdb5d54 100644 --- a/forc/src/ops/forc_build.rs +++ b/forc/src/ops/forc_build.rs @@ -1,7 +1,7 @@ use crate::cli::BuildCommand; use forc_pkg as pkg; use forc_util::ForcResult; -use pkg::{manifest::build_profile::ExperimentalFlags, MemberFilter}; +use pkg::MemberFilter; pub fn build(cmd: BuildCommand) -> ForcResult { let opts = opts_from_cmd(cmd); @@ -43,8 +43,7 @@ fn opts_from_cmd(cmd: BuildCommand) -> pkg::BuildOpts { build_target: cmd.build.build_target, tests: cmd.tests, member_filter: MemberFilter::default(), - experimental: ExperimentalFlags { - new_encoding: !cmd.no_encoding_v1, - }, + experimental: cmd.experimental.experimental, + no_experimental: cmd.experimental.no_experimental, } } diff --git a/forc/src/ops/forc_check.rs b/forc/src/ops/forc_check.rs index e1419c90982..116b27ace6d 100644 --- a/forc/src/ops/forc_check.rs +++ b/forc/src/ops/forc_check.rs @@ -4,7 +4,7 @@ use forc_pkg as pkg; use forc_pkg::manifest::GenericManifestFile; use pkg::manifest::ManifestFile; use std::path::PathBuf; -use sway_core::{language::ty, Engines, ExperimentalFlags}; +use sway_core::{language::ty, Engines}; use sway_error::handler::Handler; pub fn check(command: CheckCommand, engines: &Engines) -> Result<(Option, Handler)> { @@ -16,7 +16,8 @@ pub fn check(command: CheckCommand, engines: &Engines) -> Result<(Option Result<(Option pkg::BuildOpts { build_target: BuildTarget::default(), tests: false, member_filter: pkg::MemberFilter::only_contracts(), - experimental: ExperimentalFlags { - new_encoding: !cmd.no_encoding_v1, - }, + experimental: cmd.experimental.experimental.clone(), + no_experimental: cmd.experimental.no_experimental.clone(), } } diff --git a/forc/src/ops/forc_predicate_root.rs b/forc/src/ops/forc_predicate_root.rs index 1381aec392e..34be42c4117 100644 --- a/forc/src/ops/forc_predicate_root.rs +++ b/forc/src/ops/forc_predicate_root.rs @@ -1,7 +1,6 @@ use crate::cli::PredicateRootCommand; use anyhow::Result; use forc_pkg::{self as pkg, build_with_options}; -use pkg::manifest::build_profile::ExperimentalFlags; use sway_core::BuildTarget; pub fn predicate_root(command: PredicateRootCommand) -> Result<()> { @@ -47,8 +46,7 @@ fn build_opts_from_cmd(cmd: PredicateRootCommand) -> pkg::BuildOpts { build_target: BuildTarget::default(), tests: false, member_filter: pkg::MemberFilter::only_predicates(), - experimental: ExperimentalFlags { - new_encoding: !cmd.no_encoding_v1, - }, + experimental: cmd.experimental.experimental, + no_experimental: cmd.experimental.no_experimental, } } diff --git a/sway-core/Cargo.toml b/sway-core/Cargo.toml index 668959fab61..3a351390cbb 100644 --- a/sway-core/Cargo.toml +++ b/sway-core/Cargo.toml @@ -38,6 +38,7 @@ sha2.workspace = true strum = { workspace = true, features = ["derive"] } sway-ast.workspace = true sway-error.workspace = true +sway-features.workspace = true sway-ir.workspace = true sway-parse.workspace = true sway-types.workspace = true diff --git a/sway-core/src/asm_generation/fuel/analyses.rs b/sway-core/src/asm_generation/fuel/analyses.rs index cfd4cb931d6..9bdcac0d489 100644 --- a/sway-core/src/asm_generation/fuel/analyses.rs +++ b/sway-core/src/asm_generation/fuel/analyses.rs @@ -17,9 +17,9 @@ use crate::asm_lang::{ControlFlowOp, Label, Op, VirtualRegister}; /// Two tables are generated: `live_in` and `live_out`. Each row in the tables corresponds to an /// instruction in the program. /// * A virtual register is in the `live_out` table for a given instruction if it is live on any -/// of that node's out-edges +/// of that node's out-edges /// * A virtual register is in the `live_in` table for a given instruction if it is live on any -/// of that node's in-edges +/// of that node's in-edges /// /// /// Algorithm: diff --git a/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs b/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs index 86ecf9037f7..5c98c09411e 100644 --- a/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs +++ b/sway-core/src/asm_generation/fuel/fuel_asm_builder.rs @@ -244,9 +244,7 @@ impl<'ir, 'eng> AsmBuilder for FuelAsmBuilder<'ir, 'eng> { entries, non_entries, reg_seqr, - crate::ExperimentalFlags { - new_encoding: context.experimental.new_encoding, - }, + context.experimental, ); // Compiled dependencies will not have any content and we diff --git a/sway-core/src/asm_generation/fuel/programs/abstract.rs b/sway-core/src/asm_generation/fuel/programs/abstract.rs index da1c7cf9fc0..3256da242bb 100644 --- a/sway-core/src/asm_generation/fuel/programs/abstract.rs +++ b/sway-core/src/asm_generation/fuel/programs/abstract.rs @@ -17,10 +17,10 @@ use crate::{ VirtualImmediate18, VirtualImmediate24, }, decl_engine::DeclRefFunction, - ExperimentalFlags, }; use either::Either; use sway_error::error::CompileError; +use sway_features::ExperimentalFeatures; /// The entry point of an abstract program. pub(crate) struct AbstractEntry { @@ -44,7 +44,7 @@ pub(crate) struct AbstractProgram { entries: Vec, non_entries: Vec, reg_seqr: RegisterSequencer, - experimental: ExperimentalFlags, + experimental: ExperimentalFeatures, } impl AbstractProgram { @@ -57,7 +57,7 @@ impl AbstractProgram { entries: Vec, non_entries: Vec, reg_seqr: RegisterSequencer, - experimental: ExperimentalFlags, + experimental: ExperimentalFeatures, ) -> Self { AbstractProgram { kind, diff --git a/sway-core/src/asm_generation/fuel/register_allocator.rs b/sway-core/src/asm_generation/fuel/register_allocator.rs index 8e02d2d29fa..b3304563222 100644 --- a/sway-core/src/asm_generation/fuel/register_allocator.rs +++ b/sway-core/src/asm_generation/fuel/register_allocator.rs @@ -121,8 +121,8 @@ impl RegisterPool { /// add edges (v, b_1), ..., (v, b_n) for any b_i different from c. /// 3. for non-MOVE def of virtual register v with live_out virtual registers b_1, ..., b_n: /// add edges (v, b_1), ..., (v, b_n) -/// =============================================================================================== /// +/// =============================================================================================== pub(crate) fn create_interference_graph( ops: &[Op], live_out: &[BTreeSet], @@ -191,8 +191,8 @@ pub(crate) fn create_interference_graph( /// * When two registers are coalesced, a new node with a new virtual register (generated using the /// register sequencer) is created in the interference graph. /// * When a MOVE instruction is removed, the offset of each subsequent instruction has to be -/// updated, as well as the immediate values for some or all jump instructions (`ji`, `jnei`, and -/// `jnzi for now). +/// updated, as well as the immediate values for some or all jump instructions (`ji`, `jnei`, and +/// `jnzi for now). /// pub(crate) fn coalesce_registers( ops: &[Op], @@ -386,6 +386,7 @@ fn compute_def_use_points(ops: &[Op]) -> FxHashMap, /// 3. If some vertex n still has k or more neighbors, then the graph may not be k colorable. /// We still add it to the stack as is, as a potential spill. When popping, if we still /// can't colour it, then it becomes an actual spill. +/// /// =============================================================================================== /// pub(crate) fn color_interference_graph( diff --git a/sway-core/src/build_config.rs b/sway-core/src/build_config.rs index ba96971d4a7..711ceb014d4 100644 --- a/sway-core/src/build_config.rs +++ b/sway-core/src/build_config.rs @@ -189,7 +189,6 @@ pub struct BuildConfig { pub(crate) optimization_level: OptLevel, pub time_phases: bool, pub metrics_outfile: Option, - pub experimental: ExperimentalFlags, pub lsp_mode: Option, } @@ -237,9 +236,6 @@ impl BuildConfig { time_phases: false, metrics_outfile: None, optimization_level: OptLevel::Opt0, - experimental: ExperimentalFlags { - new_encoding: false, - }, lsp_mode: None, } } @@ -310,13 +306,6 @@ impl BuildConfig { } } - pub fn with_experimental(self, experimental: ExperimentalFlags) -> Self { - Self { - experimental, - ..self - } - } - pub fn with_lsp_mode(self, lsp_mode: Option) -> Self { Self { lsp_mode, ..self } } @@ -326,11 +315,6 @@ impl BuildConfig { } } -#[derive(Debug, Clone, Copy)] -pub struct ExperimentalFlags { - pub new_encoding: bool, -} - #[derive(Clone, Debug, Default)] pub struct LspConfig { // This is set to true if compilation was triggered by a didChange LSP event. In this case, we diff --git a/sway-core/src/ir_generation.rs b/sway-core/src/ir_generation.rs index dc08281c82e..10df2f19a9b 100644 --- a/sway-core/src/ir_generation.rs +++ b/sway-core/src/ir_generation.rs @@ -13,6 +13,7 @@ use std::{ }; use sway_error::error::CompileError; +use sway_features::ExperimentalFeatures; use sway_ir::{Context, Function, Kind, Module}; use sway_types::{span::Span, Ident}; @@ -23,7 +24,7 @@ use crate::{ language::ty, metadata::MetadataManager, types::{LogId, MessageId}, - Engines, ExperimentalFlags, TypeId, + Engines, TypeId, }; type FnKey = u64; @@ -120,7 +121,7 @@ pub fn compile_program<'eng>( program: &ty::TyProgram, include_tests: bool, engines: &'eng Engines, - experimental: ExperimentalFlags, + experimental: ExperimentalFeatures, ) -> Result, Vec> { let declaration_engine = engines.de(); @@ -148,12 +149,7 @@ pub fn compile_program<'eng>( .map(|(message_id, type_id)| (*type_id, *message_id)) .collect(); - let mut ctx = Context::new( - engines.se(), - sway_ir::ExperimentalFlags { - new_encoding: experimental.new_encoding, - }, - ); + let mut ctx = Context::new(engines.se(), experimental); ctx.program_kind = match kind { ty::TyProgramKind::Script { .. } => Kind::Script, ty::TyProgramKind::Predicate { .. } => Kind::Predicate, diff --git a/sway-core/src/ir_generation/const_eval.rs b/sway-core/src/ir_generation/const_eval.rs index ed6b6d70970..ad2d5a307ff 100644 --- a/sway-core/src/ir_generation/const_eval.rs +++ b/sway-core/src/ir_generation/const_eval.rs @@ -1370,6 +1370,7 @@ fn const_eval_intrinsic( mod tests { use super::*; use sway_error::handler::Handler; + use sway_features::ExperimentalFeatures; use sway_ir::Kind; /// This function validates if an expression can be converted to [Constant]. @@ -1388,12 +1389,7 @@ mod tests { fn assert_is_constant(is_constant: bool, prefix: &str, expr: &str) { let engines = Engines::default(); let handler = Handler::default(); - let mut context = Context::new( - engines.se(), - sway_ir::ExperimentalFlags { - new_encoding: false, - }, - ); + let mut context = Context::new(engines.se(), ExperimentalFeatures::default()); let mut md_mgr = MetadataManager::default(); let mut core_lib = namespace::Root::from(namespace::Module::new( sway_types::Ident::new_no_span("assert_is_constant_test".to_string()), @@ -1409,6 +1405,7 @@ mod tests { None, "test", None, + ExperimentalFeatures::default(), ); let (errors, _warnings) = handler.consume(); diff --git a/sway-core/src/language/parsed/expression/scrutinee.rs b/sway-core/src/language/parsed/expression/scrutinee.rs index 20384fb446b..e700c60e7d8 100644 --- a/sway-core/src/language/parsed/expression/scrutinee.rs +++ b/sway-core/src/language/parsed/expression/scrutinee.rs @@ -250,7 +250,6 @@ impl Scrutinee { let name = vec![TypeInfo::Custom { qualified_call_path: struct_name.clone().into(), type_arguments: None, - root_type_id: None, }]; let fields = fields .iter() @@ -271,7 +270,6 @@ impl Scrutinee { let name = vec![TypeInfo::Custom { qualified_call_path: enum_name.clone().into(), type_arguments: None, - root_type_id: None, }]; let value = value.gather_approximate_typeinfo_dependencies(); [name, value].concat() diff --git a/sway-core/src/language/ty/expression/reassignment.rs b/sway-core/src/language/ty/expression/reassignment.rs index 358855a0102..54a8e740c5c 100644 --- a/sway-core/src/language/ty/expression/reassignment.rs +++ b/sway-core/src/language/ty/expression/reassignment.rs @@ -47,6 +47,7 @@ pub enum TyReassignmentTarget { /// E.g.: /// - *my_ref /// - **if x > 0 { &mut &mut a } else { &mut &mut b } + /// /// The [TyExpression] is guaranteed to be of [TyExpressionVariant::Deref]. Deref(Box), } diff --git a/sway-core/src/language/ty/program.rs b/sway-core/src/language/ty/program.rs index b2257451574..5a7bd193e42 100644 --- a/sway-core/src/language/ty/program.rs +++ b/sway-core/src/language/ty/program.rs @@ -7,13 +7,14 @@ use crate::{ transform::AllowDeprecatedState, type_system::*, types::*, - Engines, ExperimentalFlags, + Engines, }; use sway_error::{ error::{CompileError, TypeNotAllowedReason}, handler::{ErrorEmitted, Handler}, }; +use sway_features::ExperimentalFeatures; use sway_types::*; #[derive(Debug, Clone)] @@ -64,7 +65,7 @@ impl TyProgram { root: &TyModule, kind: parsed::TreeType, package_name: &str, - experimental: ExperimentalFlags, + experimental: ExperimentalFeatures, ) -> Result<(TyProgramKind, Vec, Vec), ErrorEmitted> { // Extract program-kind-specific properties from the root nodes. diff --git a/sway-core/src/lib.rs b/sway-core/src/lib.rs index 0feda2ce733..ac6b7d54d34 100644 --- a/sway-core/src/lib.rs +++ b/sway-core/src/lib.rs @@ -43,6 +43,7 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; use sway_ast::AttributeDecl; use sway_error::handler::{ErrorEmitted, Handler}; +use sway_features::ExperimentalFeatures; use sway_ir::{ create_o1_pass_group, register_known_passes, Context, Kind, Module, PassGroup, PassManager, PrintPassesOpts, ARG_DEMOTION_NAME, CONST_DEMOTION_NAME, DCE_NAME, FN_DCE_NAME, @@ -70,7 +71,6 @@ pub mod fuel_prelude { pub use fuel_vm::{self, fuel_asm, fuel_crypto, fuel_tx, fuel_types}; } -pub use build_config::ExperimentalFlags; pub use engine_threading::Engines; /// Given an input `Arc` and an optional [BuildConfig], parse the input into a [lexed::LexedProgram] and [parsed::ParseProgram]. @@ -91,16 +91,10 @@ pub fn parse( handler: &Handler, engines: &Engines, config: Option<&BuildConfig>, + experimental: ExperimentalFeatures, ) -> Result<(lexed::LexedProgram, parsed::ParseProgram), ErrorEmitted> { match config { - None => parse_in_memory( - handler, - engines, - input, - ExperimentalFlags { - new_encoding: false, - }, - ), + None => parse_in_memory(handler, engines, input, experimental), // When a `BuildConfig` is given, // the module source may declare `dep`s that must be parsed from other files. Some(config) => parse_module_tree( @@ -111,7 +105,7 @@ pub fn parse( None, config.build_target, config.include_tests, - config.experimental, + experimental, config.lsp_mode.as_ref(), ) .map( @@ -200,7 +194,7 @@ fn parse_in_memory( handler: &Handler, engines: &Engines, src: Arc, - experimental: ExperimentalFlags, + experimental: ExperimentalFeatures, ) -> Result<(lexed::LexedProgram, parsed::ParseProgram), ErrorEmitted> { let mut hasher = DefaultHasher::new(); src.hash(&mut hasher); @@ -256,7 +250,7 @@ fn parse_submodules( module_dir: &Path, build_target: BuildTarget, include_tests: bool, - experimental: ExperimentalFlags, + experimental: ExperimentalFeatures, lsp_mode: Option<&LspConfig>, ) -> Submodules { // Assume the happy path, so there'll be as many submodules as dependencies, but no more. @@ -341,7 +335,7 @@ fn parse_module_tree( module_name: Option<&str>, build_target: BuildTarget, include_tests: bool, - experimental: ExperimentalFlags, + experimental: ExperimentalFeatures, lsp_mode: Option<&LspConfig>, ) -> Result { let query_engine = engines.qe(); @@ -549,6 +543,7 @@ pub fn build_module_dep_graph( pub struct CompiledAsm(pub FinalizedAsm); +#[allow(clippy::too_many_arguments)] pub fn parsed_to_ast( handler: &Handler, engines: &Engines, @@ -557,12 +552,8 @@ pub fn parsed_to_ast( build_config: Option<&BuildConfig>, package_name: &str, retrigger_compilation: Option>, + experimental: ExperimentalFeatures, ) -> Result { - let experimental = build_config - .map(|x| x.experimental) - .unwrap_or(ExperimentalFlags { - new_encoding: false, - }); let lsp_config = build_config.map(|x| x.lsp_mode.clone()).unwrap_or_default(); // Build the dependency graph for the submodules. @@ -582,6 +573,7 @@ pub fn parsed_to_ast( namespace, package_name, build_config, + experimental, ); check_should_abort(handler, retrigger_compilation.clone())?; @@ -661,12 +653,7 @@ pub fn parsed_to_ast( }; // Evaluate const declarations, to allow storage slots initialization with consts. - let mut ctx = Context::new( - engines.se(), - sway_ir::ExperimentalFlags { - new_encoding: experimental.new_encoding, - }, - ); + let mut ctx = Context::new(engines.se(), experimental); let mut md_mgr = MetadataManager::default(); let module = Module::new(&mut ctx, Kind::Contract); if let Err(e) = ir_generation::compile::compile_constants( @@ -721,6 +708,7 @@ pub fn parsed_to_ast( Ok(typed_program_with_storage_slots) } +#[allow(clippy::too_many_arguments)] pub fn compile_to_ast( handler: &Handler, engines: &Engines, @@ -729,6 +717,7 @@ pub fn compile_to_ast( build_config: Option<&BuildConfig>, package_name: &str, retrigger_compilation: Option>, + experimental: ExperimentalFeatures, ) -> Result { check_should_abort(handler, retrigger_compilation.clone())?; @@ -753,7 +742,7 @@ pub fn compile_to_ast( let parse_program_opt = time_expr!( "parse the program to a concrete syntax tree (CST)", "parse_cst", - parse(input, handler, engines, build_config), + parse(input, handler, engines, build_config, experimental), build_config, metrics ); @@ -785,6 +774,7 @@ pub fn compile_to_ast( build_config, package_name, retrigger_compilation.clone(), + experimental ), build_config, metrics @@ -820,6 +810,7 @@ pub fn compile_to_asm( initial_namespace: &mut namespace::Root, build_config: &BuildConfig, package_name: &str, + experimental: ExperimentalFeatures, ) -> Result { let ast_res = compile_to_ast( handler, @@ -829,8 +820,9 @@ pub fn compile_to_asm( Some(build_config), package_name, None, + experimental, )?; - ast_to_asm(handler, engines, &ast_res, build_config) + ast_to_asm(handler, engines, &ast_res, build_config, experimental) } /// Given an AST compilation result, try compiling to a `CompiledAsm`, @@ -840,19 +832,22 @@ pub fn ast_to_asm( engines: &Engines, programs: &Programs, build_config: &BuildConfig, + experimental: ExperimentalFeatures, ) -> Result { let typed_program = match &programs.typed { Ok(typed_program) => typed_program, Err(err) => return Err(*err), }; - let asm = match compile_ast_to_ir_to_asm(handler, engines, typed_program, build_config) { - Ok(res) => res, - Err(err) => { - handler.dedup(); - return Err(err); - } - }; + let asm = + match compile_ast_to_ir_to_asm(handler, engines, typed_program, build_config, experimental) + { + Ok(res) => res, + Err(err) => { + handler.dedup(); + return Err(err); + } + }; Ok(CompiledAsm(asm)) } @@ -861,6 +856,7 @@ pub(crate) fn compile_ast_to_ir_to_asm( engines: &Engines, program: &ty::TyProgram, build_config: &BuildConfig, + experimental: ExperimentalFeatures, ) -> Result { // The IR pipeline relies on type information being fully resolved. // If type information is found to still be generic or unresolved inside of @@ -877,7 +873,7 @@ pub(crate) fn compile_ast_to_ir_to_asm( program, build_config.include_tests, engines, - build_config.experimental, + experimental, ) { Ok(ir) => ir, Err(errors) => { @@ -982,6 +978,7 @@ pub fn compile_to_bytecode( build_config: &BuildConfig, source_map: &mut SourceMap, package_name: &str, + experimental: ExperimentalFeatures, ) -> Result { let asm_res = compile_to_asm( handler, @@ -990,6 +987,7 @@ pub fn compile_to_bytecode( initial_namespace, build_config, package_name, + experimental, )?; asm_to_bytecode(handler, asm_res, source_map, engines.se(), build_config) } @@ -1232,6 +1230,7 @@ fn test_basic_prog() { &handler, &engines, None, + ExperimentalFeatures::default(), ); prog.unwrap(); } @@ -1251,6 +1250,7 @@ fn test_parenthesized() { &handler, &engines, None, + ExperimentalFeatures::default(), ); prog.unwrap(); } @@ -1272,6 +1272,7 @@ fn test_unary_ordering() { &handler, &engines, None, + ExperimentalFeatures::default(), ); let (.., prog) = prog.unwrap(); // this should parse as `(!a) && b`, not `!(a && b)`. So, the top level @@ -1320,6 +1321,7 @@ fn test_parser_recovery() { &handler, &engines, None, + ExperimentalFeatures::default(), ); let (_, _) = prog.unwrap(); assert!(handler.has_errors()); diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/impl_trait.rs b/sway-core/src/semantic_analysis/ast_node/declaration/impl_trait.rs index 033fe5cff9b..8d88a8989d8 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/impl_trait.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/impl_trait.rs @@ -1,3 +1,4 @@ +#![allow(clippy::mutable_key_type)] use std::{ collections::{BTreeMap, HashMap, HashSet}, sync::Arc, diff --git a/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/mod.rs b/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/mod.rs index c974226d2b7..b547768d094 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/mod.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/mod.rs @@ -20,12 +20,12 @@ //! for a particular match branch (arm): //! - branch condition: Overall condition that must be `true` for the branch to match. //! - result variable declarations: Variable declarations that needs to be added to the -//! match branch result, before the actual body. Here we distinguish between the variables -//! actually declared in the match arm pattern and so called "tuple variables" that are -//! compiler generated and contain values for variables extracted out of individual OR variants. +//! match branch result, before the actual body. Here we distinguish between the variables +//! actually declared in the match arm pattern and so called "tuple variables" that are +//! compiler generated and contain values for variables extracted out of individual OR variants. //! - OR variant index variables: Variable declarations that are generated in case of having -//! variables in OR patterns. Index variables hold 1-based index of the OR variant being matched -//! or zero if non of the OR variants has matched. +//! variables in OR patterns. Index variables hold 1-based index of the OR variant being matched +//! or zero if non of the OR variants has matched. //! //! Afterwards, these three artifacts coming from every individual branch are glued together in the //! [crate::ty::TyMatchExpression] to form the final desugaring. diff --git a/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_match_branch.rs b/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_match_branch.rs index f8b530e2107..7e110b86e49 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_match_branch.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/match_expression/typed/typed_match_branch.rs @@ -252,12 +252,12 @@ type CarryOverTupleDeclarations = Vec; /// via [ty::TyMatchBranch]: /// - branch condition: Overall condition that must be `true` for the branch to match. /// - result variable declarations: Variable declarations that needs to be added to the -/// match branch result, before the actual body. Here we distinguish between the variables -/// actually declared in the match arm pattern and so called "tuple variables" that are -/// compiler generated and contain values for variables extracted out of individual OR variants. +/// match branch result, before the actual body. Here we distinguish between the variables +/// actually declared in the match arm pattern and so called "tuple variables" that are +/// compiler generated and contain values for variables extracted out of individual OR variants. /// - OR variant index variables: Variable declarations that are generated in case of having -/// variables in OR patterns. Index variables hold 1-based index of the OR variant being matched -/// or zero if non of the OR variants has matched. +/// variables in OR patterns. Index variables hold 1-based index of the OR variant being matched +/// or zero if non of the OR variants has matched. /// /// ## Algorithm Overview /// The algorithm traverses the `req_decl_tree` bottom up from left to right and collects the diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs index af790c3d2ed..51672758fba 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression.rs @@ -995,6 +995,7 @@ impl ty::TyExpression { /// Returns the position of the first match arm that is an "interior" arm, meaning: /// - arm is a catch-all arm /// - arm is not the last match arm + /// /// or `None` if such arm does not exist. /// Note that the arm can be the first arm. fn interior_catch_all_arm_position(arms_reachability: &[ReachableReport]) -> Option { @@ -1357,7 +1358,6 @@ impl ty::TyExpression { let type_info = TypeInfo::Custom { qualified_call_path: qualified_call_path.clone(), type_arguments: None, - root_type_id: None, }; TypeBinding { @@ -1490,7 +1490,6 @@ impl ty::TyExpression { let type_info = type_name_to_type_info_opt(&type_name).unwrap_or(TypeInfo::Custom { qualified_call_path: type_name.clone().into(), type_arguments: None, - root_type_id: None, }); let method_name_binding = TypeBinding { @@ -1732,7 +1731,6 @@ impl ty::TyExpression { type_name_to_type_info_opt(type_name).unwrap_or(TypeInfo::Custom { qualified_call_path: type_name.clone().into(), type_arguments: None, - root_type_id: None, }) }); @@ -2899,7 +2897,7 @@ fn check_asm_block_validity( #[cfg(test)] mod tests { use super::*; - use crate::{Engines, ExperimentalFlags}; + use crate::{Engines, ExperimentalFeatures}; use sway_error::type_error::TypeError; use symbol_collection_context::SymbolCollectionContext; @@ -2908,7 +2906,7 @@ mod tests { engines: &Engines, expr: &Expression, type_annotation: TypeId, - experimental: ExperimentalFlags, + experimental: ExperimentalFeatures, ) -> Result { let collection_ctx_ns = Namespace::new(); let mut collection_ctx = SymbolCollectionContext::new(collection_ctx_ns); @@ -2952,9 +2950,7 @@ mod tests { ), None, ), - ExperimentalFlags { - new_encoding: false, - }, + ExperimentalFeatures::default(), )?; expr.type_check_analyze(handler, &mut TypeCheckAnalysisContext::new(&engines))?; Ok(expr) @@ -3094,9 +3090,7 @@ mod tests { ), None, ), - ExperimentalFlags { - new_encoding: false, - }, + ExperimentalFeatures::default(), ); let (errors, warnings) = handler.consume(); assert!(comp_res.is_ok()); diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs index 2619ab21664..3c118c43536 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/method_application.rs @@ -649,7 +649,6 @@ pub(crate) fn type_check_method_application( if let TypeInfo::Custom { qualified_call_path, type_arguments, - root_type_id: _, } = &*type_engine.get(t.initial_type_id) { let mut subst_type_parameters = vec![]; diff --git a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/struct_instantiation.rs b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/struct_instantiation.rs index 23ff00f7894..1d84a623fbd 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/struct_instantiation.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/typed_expression/struct_instantiation.rs @@ -69,12 +69,10 @@ pub(crate) fn struct_instantiation( (_, true) => TypeInfo::Custom { qualified_call_path: suffix.clone().into(), type_arguments: None, - root_type_id: None, }, (_, false) => TypeInfo::Custom { qualified_call_path: suffix.clone().into(), type_arguments: Some(type_arguments), - root_type_id: None, }, }; diff --git a/sway-core/src/semantic_analysis/namespace/contract_helpers.rs b/sway-core/src/semantic_analysis/namespace/contract_helpers.rs index 822d4262156..af8a84cbac6 100644 --- a/sway-core/src/semantic_analysis/namespace/contract_helpers.rs +++ b/sway-core/src/semantic_analysis/namespace/contract_helpers.rs @@ -29,7 +29,7 @@ pub fn default_with_contract_id( name: Ident, visibility: Visibility, contract_id_value: String, - experimental: crate::ExperimentalFlags, + experimental: crate::ExperimentalFeatures, ) -> Result> { let handler = <_>::default(); default_with_contract_id_inner( @@ -55,7 +55,7 @@ fn default_with_contract_id_inner( ns_name: Ident, visibility: Visibility, contract_id_value: String, - experimental: crate::ExperimentalFlags, + experimental: crate::ExperimentalFeatures, ) -> Result { // it would be nice to one day maintain a span from the manifest file, but // we don't keep that around so we just use the span from the generated const decl instead. diff --git a/sway-core/src/semantic_analysis/namespace/trait_map.rs b/sway-core/src/semantic_analysis/namespace/trait_map.rs index 1fb39cf2982..6b966d16b5a 100644 --- a/sway-core/src/semantic_analysis/namespace/trait_map.rs +++ b/sway-core/src/semantic_analysis/namespace/trait_map.rs @@ -1404,7 +1404,6 @@ impl TraitMap { } else { Some(suffix.args.to_vec()) }, - root_type_id: None, }, suffix.name.span().source_id(), ); @@ -1431,7 +1430,6 @@ impl TraitMap { } else { Some(constraint_type_arguments.clone()) }, - root_type_id: None, }, constraint_trait_name.span().source_id(), ); @@ -1472,7 +1470,6 @@ impl TraitMap { if let TypeInfo::Custom { qualified_call_path: _, type_arguments: Some(type_arguments), - root_type_id: _, } = &*type_engine.get(*constraint_type_id) { type_arguments_string = format!("<{}>", engines.help_out(type_arguments)); diff --git a/sway-core/src/semantic_analysis/node_dependencies.rs b/sway-core/src/semantic_analysis/node_dependencies.rs index c34feeab4e6..e0a5b5b3f0b 100644 --- a/sway-core/src/semantic_analysis/node_dependencies.rs +++ b/sway-core/src/semantic_analysis/node_dependencies.rs @@ -791,21 +791,14 @@ impl Dependencies { TypeInfo::Custom { qualified_call_path: name, type_arguments, - root_type_id, } => { self.deps .insert(DependentSymbol::Symbol(name.clone().call_path.suffix)); - let s = match type_arguments { + match type_arguments { Some(type_arguments) => { self.gather_from_type_arguments(engines, type_arguments) } None => self, - }; - match root_type_id { - Some(root_type_id) => { - s.gather_from_typeinfo(engines, &engines.te().get(*root_type_id)) - } - None => s, } } TypeInfo::Tuple(elems) => self.gather_from_iter(elems.iter(), |deps, elem| { diff --git a/sway-core/src/semantic_analysis/program.rs b/sway-core/src/semantic_analysis/program.rs index 581a630d9b6..8a12705d021 100644 --- a/sway-core/src/semantic_analysis/program.rs +++ b/sway-core/src/semantic_analysis/program.rs @@ -11,6 +11,7 @@ use crate::{ BuildConfig, Engines, }; use sway_error::handler::{ErrorEmitted, Handler}; +use sway_features::ExperimentalFeatures; use sway_ir::{Context, Module}; use super::{ @@ -40,6 +41,7 @@ impl TyProgram { /// /// The given `namespace` acts as an initial state for each module within this program. /// It should contain a submodule for each library package dependency. + #[allow(clippy::too_many_arguments)] pub fn type_check( handler: &Handler, engines: &Engines, @@ -48,14 +50,8 @@ impl TyProgram { mut namespace: namespace::Namespace, package_name: &str, build_config: Option<&BuildConfig>, + experimental: ExperimentalFeatures, ) -> Result { - let experimental = - build_config - .map(|x| x.experimental) - .unwrap_or(crate::ExperimentalFlags { - new_encoding: false, - }); - let mut ctx = TypeCheckContext::from_root(&mut namespace, collection_ctx, engines, experimental) .with_kind(parsed.kind); diff --git a/sway-core/src/semantic_analysis/type_check_context.rs b/sway-core/src/semantic_analysis/type_check_context.rs index 455cc21fb4b..4a7e6ccf852 100644 --- a/sway-core/src/semantic_analysis/type_check_context.rs +++ b/sway-core/src/semantic_analysis/type_check_context.rs @@ -1,7 +1,7 @@ +#![allow(clippy::mutable_key_type)] use std::collections::{HashMap, VecDeque}; use crate::{ - build_config::ExperimentalFlags, decl_engine::{DeclEngineGet, DeclRefFunction}, engine_threading::*, language::{ @@ -25,6 +25,7 @@ use sway_error::{ error::CompileError, handler::{ErrorEmitted, Handler}, }; +use sway_features::ExperimentalFeatures; use sway_types::{span::Span, Ident, Spanned}; use super::{ @@ -48,7 +49,7 @@ pub struct TypeCheckContext<'a> { pub(crate) engines: &'a Engines, /// Set of experimental flags. - pub(crate) experimental: ExperimentalFlags, + pub(crate) experimental: ExperimentalFeatures, /// Keeps the accumulated symbols previously collected. pub(crate) collection_ctx: &'a mut SymbolCollectionContext, @@ -117,7 +118,7 @@ impl<'a> TypeCheckContext<'a> { namespace: &'a mut Namespace, collection_ctx: &'a mut SymbolCollectionContext, engines: &'a Engines, - experimental: ExperimentalFlags, + experimental: ExperimentalFeatures, ) -> Self { Self { namespace, @@ -152,7 +153,7 @@ impl<'a> TypeCheckContext<'a> { root_namespace: &'a mut Namespace, collection_ctx: &'a mut SymbolCollectionContext, engines: &'a Engines, - experimental: ExperimentalFlags, + experimental: ExperimentalFeatures, ) -> Self { Self::from_module_namespace(root_namespace, collection_ctx, engines, experimental) } @@ -161,7 +162,7 @@ impl<'a> TypeCheckContext<'a> { namespace: &'a mut Namespace, collection_ctx: &'a mut SymbolCollectionContext, engines: &'a Engines, - experimental: ExperimentalFlags, + experimental: ExperimentalFeatures, ) -> Self { Self { collection_ctx, @@ -880,7 +881,6 @@ impl<'a> TypeCheckContext<'a> { if let TypeInfo::Custom { qualified_call_path: call_path, type_arguments, - root_type_id: _, } = &*type_engine.get(as_trait) { qualified_call_path = Some(call_path.clone()); diff --git a/sway-core/src/semantic_analysis/type_resolve.rs b/sway-core/src/semantic_analysis/type_resolve.rs index f2498364acf..e4c676fda1e 100644 --- a/sway-core/src/semantic_analysis/type_resolve.rs +++ b/sway-core/src/semantic_analysis/type_resolve.rs @@ -38,31 +38,17 @@ pub fn resolve_type( TypeInfo::Custom { qualified_call_path, type_arguments, - root_type_id, } => { - let type_decl_opt = if let Some(root_type_id) = root_type_id { - resolve_call_path_and_root_type_id( - handler, - engines, - namespace.module(engines), - root_type_id, - None, - &qualified_call_path.clone().to_call_path(handler)?, - self_type, - ) - .ok() - } else { - resolve_qualified_call_path( - handler, - engines, - namespace, - module_path, - &qualified_call_path, - self_type, - subst_ctx, - ) - .ok() - }; + let type_decl_opt = resolve_qualified_call_path( + handler, + engines, + namespace, + module_path, + &qualified_call_path, + self_type, + subst_ctx, + ) + .ok(); type_decl_opt_to_type_id( handler, engines, @@ -368,6 +354,9 @@ pub fn decl_to_type_info( } (*engines.te().get(type_decl.ty.clone().unwrap().type_id)).clone() } + ty::TyDecl::GenericTypeForFunctionScope(decl) => { + (*engines.te().get(decl.type_id)).clone() + } _ => { return Err(handler.emit_err(CompileError::SymbolNotFound { name: symbol.clone(), diff --git a/sway-core/src/transform/attribute.rs b/sway-core/src/transform/attribute.rs index 46d2acde691..d29978205c8 100644 --- a/sway-core/src/transform/attribute.rs +++ b/sway-core/src/transform/attribute.rs @@ -24,8 +24,7 @@ use indexmap::IndexMap; use sway_ast::Literal; use sway_types::{ constants::{ - ALLOW_DEAD_CODE_NAME, ALLOW_DEPRECATED_NAME, CFG_EXPERIMENTAL_NEW_ENCODING, - CFG_PROGRAM_TYPE_ARG_NAME, CFG_TARGET_ARG_NAME, + ALLOW_DEAD_CODE_NAME, ALLOW_DEPRECATED_NAME, CFG_PROGRAM_TYPE_ARG_NAME, CFG_TARGET_ARG_NAME, }, Ident, Span, Spanned, }; @@ -92,11 +91,14 @@ impl AttributeKind { ALLOW_DEAD_CODE_NAME.to_string(), ALLOW_DEPRECATED_NAME.to_string(), ]), - Cfg => Some(vec![ - CFG_TARGET_ARG_NAME.to_string(), - CFG_PROGRAM_TYPE_ARG_NAME.to_string(), - CFG_EXPERIMENTAL_NEW_ENCODING.to_string(), - ]), + Cfg => { + let mut cfgs = vec![ + CFG_TARGET_ARG_NAME.to_string(), + CFG_PROGRAM_TYPE_ARG_NAME.to_string(), + ]; + cfgs.extend(sway_features::CFG.iter().map(|x| x.to_string())); + Some(cfgs) + } } } } diff --git a/sway-core/src/transform/to_parsed_lang/context.rs b/sway-core/src/transform/to_parsed_lang/context.rs index f450eb5257f..efdf030e3ae 100644 --- a/sway-core/src/transform/to_parsed_lang/context.rs +++ b/sway-core/src/transform/to_parsed_lang/context.rs @@ -1,11 +1,12 @@ +use sway_features::ExperimentalFeatures; + use crate::{ - build_config::ExperimentalFlags, language::parsed::{Declaration, TreeType}, BuildTarget, }; pub struct Context { - pub experimental: ExperimentalFlags, + pub experimental: ExperimentalFeatures, /// Indicates whether the module being parsed has a `configurable` block. module_has_configurable_block: bool, @@ -35,7 +36,7 @@ pub struct Context { impl Context { /// Create a new context. - pub fn new(build_target: BuildTarget, experimental: ExperimentalFlags) -> Self { + pub fn new(build_target: BuildTarget, experimental: ExperimentalFeatures) -> Self { Self { build_target, experimental, diff --git a/sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs b/sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs index b48cce083eb..e40c9148fda 100644 --- a/sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs +++ b/sway-core/src/transform/to_parsed_lang/convert_parse_tree.rs @@ -7,7 +7,7 @@ use crate::{ language::{parsed::*, *}, transform::{attribute::*, to_parsed_lang::context::Context}, type_system::*, - BuildTarget, Engines, ExperimentalFlags, + BuildTarget, Engines, }; use indexmap::IndexMap; @@ -29,14 +29,14 @@ use sway_ast::{ use sway_error::handler::{ErrorEmitted, Handler}; use sway_error::warning::{CompileWarning, Warning}; use sway_error::{convert_parse_tree_error::ConvertParseTreeError, error::CompileError}; +use sway_features::ExperimentalFeatures; use sway_types::{ constants::{ - ALLOW_ATTRIBUTE_NAME, CFG_ATTRIBUTE_NAME, CFG_EXPERIMENTAL_NEW_ENCODING, - CFG_PROGRAM_TYPE_ARG_NAME, CFG_TARGET_ARG_NAME, DEPRECATED_ATTRIBUTE_NAME, - DOC_ATTRIBUTE_NAME, DOC_COMMENT_ATTRIBUTE_NAME, FALLBACK_ATTRIBUTE_NAME, - INLINE_ATTRIBUTE_NAME, PAYABLE_ATTRIBUTE_NAME, STORAGE_PURITY_ATTRIBUTE_NAME, - STORAGE_PURITY_READ_NAME, STORAGE_PURITY_WRITE_NAME, TEST_ATTRIBUTE_NAME, - VALID_ATTRIBUTE_NAMES, + ALLOW_ATTRIBUTE_NAME, CFG_ATTRIBUTE_NAME, CFG_PROGRAM_TYPE_ARG_NAME, CFG_TARGET_ARG_NAME, + DEPRECATED_ATTRIBUTE_NAME, DOC_ATTRIBUTE_NAME, DOC_COMMENT_ATTRIBUTE_NAME, + FALLBACK_ATTRIBUTE_NAME, INLINE_ATTRIBUTE_NAME, PAYABLE_ATTRIBUTE_NAME, + STORAGE_PURITY_ATTRIBUTE_NAME, STORAGE_PURITY_READ_NAME, STORAGE_PURITY_WRITE_NAME, + TEST_ATTRIBUTE_NAME, VALID_ATTRIBUTE_NAMES, }, integer_bits::IntegerBits, BaseIdent, @@ -1260,7 +1260,6 @@ fn generic_params_opt_to_type_parameters_with_parent( TypeInfo::Custom { qualified_call_path: ident.clone().into(), type_arguments: None, - root_type_id: None, }, ident.span().source_id(), ); @@ -4341,7 +4340,6 @@ fn ty_to_type_parameter( TypeInfo::Custom { qualified_call_path: name_ident.clone().into(), type_arguments: None, - root_type_id: None, }, name_ident.span().source_id(), ); @@ -4625,23 +4623,12 @@ fn path_type_to_type_info( } if !suffix.is_empty() { - let (mut call_path, type_arguments) = path_type_to_call_path_and_type_arguments( + let (call_path, type_arguments) = path_type_to_call_path_and_type_arguments( context, handler, engines, path_type, )?; - - let mut root_type_id = None; - if name.as_str() == "Self" { - call_path.call_path.prefixes.remove(0); - root_type_id = Some(engines.te().insert( - engines, - type_info, - name.span().source_id(), - )); - } TypeInfo::Custom { qualified_call_path: call_path, type_arguments: Some(type_arguments), - root_type_id, } } else { type_info @@ -4686,7 +4673,6 @@ fn path_type_to_type_info( TypeInfo::Custom { qualified_call_path: call_path, type_arguments: Some(type_arguments), - root_type_id: None, } } } @@ -4862,7 +4848,7 @@ pub fn cfg_eval( context: &Context, handler: &Handler, attrs_map: &AttributesMap, - experimental: ExperimentalFlags, + experimental: ExperimentalFeatures, ) -> Result { if let Some(cfg_attrs) = attrs_map.get(&AttributeKind::Cfg) { for cfg_attr in cfg_attrs { @@ -4928,19 +4914,28 @@ pub fn cfg_eval( return Err(handler.emit_err(error.into())); } } - CFG_EXPERIMENTAL_NEW_ENCODING => match &arg.value { - Some(sway_ast::Literal::Bool(v)) => { - let is_true = matches!(v.kind, sway_ast::literal::LitBoolType::True); - return Ok(experimental.new_encoding == is_true); - } - _ => { - let error = - ConvertParseTreeError::ExpectedExperimentalNewEncodingArgValue { - span: arg.span(), - }; - return Err(handler.emit_err(error.into())); + // Check if this is a known experimental feature + cfg_experimental + if sway_features::CFG.iter().any(|x| *x == cfg_experimental) => + { + match &arg.value { + Some(sway_ast::Literal::Bool(v)) => { + let is_true = + matches!(v.kind, sway_ast::literal::LitBoolType::True); + return Ok(experimental + .is_enabled_for_cfg(cfg_experimental) + .unwrap() + == is_true); + } + _ => { + let error = + ConvertParseTreeError::UnexpectedValueForCfgExperimental { + span: arg.span(), + }; + return Err(handler.emit_err(error.into())); + } } - }, + } _ => { return Err(handler.emit_err( ConvertParseTreeError::InvalidCfgArg { diff --git a/sway-core/src/type_system/id.rs b/sway-core/src/type_system/id.rs index b1eda847745..7a7480fe838 100644 --- a/sway-core/src/type_system/id.rs +++ b/sway-core/src/type_system/id.rs @@ -1,3 +1,4 @@ +#![allow(clippy::mutable_key_type)] use indexmap::IndexMap; use sway_error::{ error::CompileError, @@ -360,7 +361,6 @@ impl TypeId { TypeInfo::Custom { qualified_call_path: _, type_arguments, - root_type_id: _, } => { if let Some(type_arguments) = type_arguments { for type_arg in type_arguments { diff --git a/sway-core/src/type_system/info.rs b/sway-core/src/type_system/info.rs index d4bcae5b8bf..29767d81fc0 100644 --- a/sway-core/src/type_system/info.rs +++ b/sway-core/src/type_system/info.rs @@ -152,11 +152,6 @@ pub enum TypeInfo { Custom { qualified_call_path: QualifiedCallPath, type_arguments: Option>, - /// When root_type_id contains some type id then the call path applies - /// to the specified root_type_id as root. - /// This is used by associated types which should produce a TypeInfo::Custom - /// such as Self::T. - root_type_id: Option, }, B256, /// This means that specific type of a number is not yet known. It will be @@ -243,11 +238,9 @@ impl HashWithEngines for TypeInfo { TypeInfo::Custom { qualified_call_path: call_path, type_arguments, - root_type_id, } => { call_path.hash(state, engines); type_arguments.as_deref().hash(state, engines); - root_type_id.hash(state); } TypeInfo::Storage { fields } => { fields.hash(state, engines); @@ -325,12 +318,10 @@ impl PartialEqWithEngines for TypeInfo { Self::Custom { qualified_call_path: l_name, type_arguments: l_type_args, - root_type_id: l_root_type_id, }, Self::Custom { qualified_call_path: r_name, type_arguments: r_type_args, - root_type_id: r_root_type_id, }, ) => { l_name.call_path.suffix == r_name.call_path.suffix @@ -338,7 +329,6 @@ impl PartialEqWithEngines for TypeInfo { .qualified_path_root .eq(&r_name.qualified_path_root, ctx) && l_type_args.as_deref().eq(&r_type_args.as_deref(), ctx) - && l_root_type_id.eq(r_root_type_id) } (Self::StringSlice, Self::StringSlice) => true, (Self::StringArray(l), Self::StringArray(r)) => l.val() == r.val(), @@ -477,12 +467,10 @@ impl OrdWithEngines for TypeInfo { Self::Custom { qualified_call_path: l_call_path, type_arguments: l_type_args, - root_type_id: l_root_type_id, }, Self::Custom { qualified_call_path: r_call_path, type_arguments: r_type_args, - root_type_id: r_root_type_id, }, ) => l_call_path .call_path @@ -493,8 +481,7 @@ impl OrdWithEngines for TypeInfo { .qualified_path_root .cmp(&r_call_path.qualified_path_root, ctx) }) - .then_with(|| l_type_args.as_deref().cmp(&r_type_args.as_deref(), ctx)) - .then_with(|| l_root_type_id.cmp(r_root_type_id)), + .then_with(|| l_type_args.as_deref().cmp(&r_type_args.as_deref(), ctx)), (Self::StringArray(l), Self::StringArray(r)) => l.val().cmp(&r.val()), (Self::UnsignedInteger(l), Self::UnsignedInteger(r)) => l.cmp(r), (Self::Enum(l_decl_id), Self::Enum(r_decl_id)) => { @@ -874,7 +861,6 @@ impl TypeInfo { qualified_path_root: None, }, type_arguments: None, - root_type_id: None, } } @@ -1286,7 +1272,6 @@ impl TypeInfo { TypeInfo::Custom { qualified_call_path: call_path, type_arguments: other_type_arguments, - root_type_id, } => { if other_type_arguments.is_some() { Err(handler @@ -1295,7 +1280,6 @@ impl TypeInfo { let type_info = TypeInfo::Custom { qualified_call_path: call_path, type_arguments: Some(type_arguments), - root_type_id, }; Ok(type_info) } diff --git a/sway-core/src/type_system/unify/occurs_check.rs b/sway-core/src/type_system/unify/occurs_check.rs index 003878f3fb7..af893ad7cfe 100644 --- a/sway-core/src/type_system/unify/occurs_check.rs +++ b/sway-core/src/type_system/unify/occurs_check.rs @@ -1,3 +1,4 @@ +#![allow(clippy::mutable_key_type)] use crate::{engine_threading::Engines, type_system::priv_prelude::*}; /// Helper struct to perform the occurs check. diff --git a/sway-core/src/type_system/unify/unify_check.rs b/sway-core/src/type_system/unify/unify_check.rs index 7479a4142ac..b1e1a9db62a 100644 --- a/sway-core/src/type_system/unify/unify_check.rs +++ b/sway-core/src/type_system/unify/unify_check.rs @@ -269,12 +269,10 @@ impl<'a> UnifyCheck<'a> { Custom { qualified_call_path: l_name, type_arguments: l_type_args, - root_type_id: l_root_type_id, }, Custom { qualified_call_path: r_name, type_arguments: r_type_args, - root_type_id: r_root_type_id, }, ) => { let l_types = l_type_args @@ -289,16 +287,6 @@ impl<'a> UnifyCheck<'a> { .iter() .map(|x| x.type_id) .collect::>(); - let l_root_type_ids = if let Some(l_root_type_id) = l_root_type_id { - vec![*l_root_type_id] - } else { - vec![] - }; - let r_root_type_ids = if let Some(r_root_type_id) = r_root_type_id { - vec![*r_root_type_id] - } else { - vec![] - }; let same_qualified_path_root = match ( l_name.qualified_path_root.clone(), r_name.qualified_path_root.clone(), @@ -318,8 +306,7 @@ impl<'a> UnifyCheck<'a> { return l_name.call_path.suffix == r_name.call_path.suffix && same_qualified_path_root - && self.check_multiple(&l_types, &r_types) - && self.check_multiple(&l_root_type_ids, &r_root_type_ids); + && self.check_multiple(&l_types, &r_types); } (Enum(l_decl_ref), Enum(r_decl_ref)) => { let l_decl = self.engines.de().get_enum(l_decl_ref); diff --git a/sway-core/src/types/collect_types_metadata.rs b/sway-core/src/types/collect_types_metadata.rs index 7f535787f9a..6e5fff30385 100644 --- a/sway-core/src/types/collect_types_metadata.rs +++ b/sway-core/src/types/collect_types_metadata.rs @@ -8,9 +8,10 @@ use std::{ sync::{Arc, Mutex}, }; -use crate::{type_system::TypeId, Engines, ExperimentalFlags}; +use crate::{type_system::TypeId, Engines}; use sha2::{Digest, Sha256}; use sway_error::handler::{ErrorEmitted, Handler}; +use sway_features::ExperimentalFeatures; use sway_types::{Ident, Span}; /// If any types contained by this node are unresolved or have yet to be inferred, throw an @@ -69,7 +70,7 @@ pub struct CollectTypesMetadataContext<'cx> { pub(crate) program_name: String, - pub experimental: ExperimentalFlags, + pub experimental: ExperimentalFeatures, } impl<'cx> CollectTypesMetadataContext<'cx> { @@ -111,7 +112,7 @@ impl<'cx> CollectTypesMetadataContext<'cx> { pub fn new( engines: &'cx Engines, - experimental: ExperimentalFlags, + experimental: ExperimentalFeatures, program_name: String, ) -> Self { let mut ctx = Self { diff --git a/sway-error/src/convert_parse_tree_error.rs b/sway-error/src/convert_parse_tree_error.rs index 6c909e2016e..8b8a96528a0 100644 --- a/sway-error/src/convert_parse_tree_error.rs +++ b/sway-error/src/convert_parse_tree_error.rs @@ -117,8 +117,8 @@ pub enum ConvertParseTreeError { InvalidCfgProgramTypeArgValue { span: Span, value: String }, #[error("Expected a value for the program_type argument")] ExpectedCfgProgramTypeArgValue { span: Span }, - #[error("Expected \"true\" or \"false\" for experimental_new_encoding")] - ExpectedExperimentalNewEncodingArgValue { span: Span }, + #[error("Expected \"true\" or \"false\" for experimental conditional compilation")] + UnexpectedValueForCfgExperimental { span: Span }, #[error("Unexpected attribute value: \"{value}\" for attribute: \"cfg\"")] InvalidCfgArg { span: Span, value: String }, } @@ -183,7 +183,7 @@ impl Spanned for ConvertParseTreeError { ConvertParseTreeError::ExpectedCfgTargetArgValue { span } => span.clone(), ConvertParseTreeError::InvalidCfgProgramTypeArgValue { span, .. } => span.clone(), ConvertParseTreeError::ExpectedCfgProgramTypeArgValue { span } => span.clone(), - ConvertParseTreeError::ExpectedExperimentalNewEncodingArgValue { span } => span.clone(), + ConvertParseTreeError::UnexpectedValueForCfgExperimental { span } => span.clone(), ConvertParseTreeError::InvalidCfgArg { span, .. } => span.clone(), } } diff --git a/sway-features/Cargo.toml b/sway-features/Cargo.toml new file mode 100644 index 00000000000..2c2ed28f414 --- /dev/null +++ b/sway-features/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "sway-features" +description = "Sway's experimental features" +version.workspace = true +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true + +[dependencies] +clap = { workspace = true, features = ["derive"] } +paste.workspace = true diff --git a/sway-features/src/lib.rs b/sway-features/src/lib.rs new file mode 100644 index 00000000000..55d50920891 --- /dev/null +++ b/sway-features/src/lib.rs @@ -0,0 +1,245 @@ +use std::collections::HashMap; + +use clap::{Parser, ValueEnum}; + +macro_rules! features { + ($($name:ident = $enabled:literal, $url:literal),* $(,)?) => { + paste::paste! { + pub const CFG: &[&str] = &[ + $( + stringify!([]), + )* + ]; + + #[derive(Copy, Clone, Debug, ValueEnum)] + #[value(rename_all = "snake")] + pub enum Feature { + $( + [<$name:camel>], + )* + } + + impl std::str::FromStr for Feature { + type Err = Error; + + fn from_str(s: &str) -> Result { + match s { + $( + stringify!([<$name:snake>]) => { + Ok(Self::[<$name:camel>]) + }, + )* + _ => Err(Error::UnknownFeature(s.to_string())), + } + } + } + + #[derive(Copy, Clone, Debug, PartialEq, Eq)] + pub struct ExperimentalFeatures { + $( + pub [<$name:snake>]: bool, + )* + } + + impl std::default::Default for ExperimentalFeatures { + fn default() -> Self { + Self { + $( + [<$name:snake>]: $enabled, + )* + } + } + } + + impl ExperimentalFeatures { + pub fn set_enabled_by_name(&mut self, feature: &str, enabled: bool) -> Result<(), Error> { + let feature = feature.trim(); + match feature { + $( + stringify!([<$name:snake>]) => { + self.[<$name:snake>] = enabled; + Ok(()) + }, + )* + "" => Ok(()), + _ => Err(Error::UnknownFeature(feature.to_string())), + } + } + + pub fn set_enabled(&mut self, feature: Feature, enabled: bool) { + match feature { + $( + Feature::[<$name:camel>] => { + self.[<$name:snake>] = enabled + }, + )* + } + } + + /// Used for testing if a `#[cfg(...)]` feature is enabled. + /// Already prepends "experimental_" to the feature name. + pub fn is_enabled_for_cfg(&self, cfg: &str) -> Result { + match cfg { + $( + stringify!([]) => Ok(self.[<$name:snake>]), + )* + _ => Err(Error::UnknownFeature(cfg.to_string())) + } + } + + $( + pub fn [](mut self, enabled: bool) -> Self { + self.[<$name:snake>] = enabled; + self + } + )* + } + } + }; +} + +impl ExperimentalFeatures { + /// Experimental features will be applied in the following order: + /// 1 - manifest (no specific order) + /// 2 - cli_no_experimental + /// 3 - cli_experimental + /// 4 - FORC_NO_EXPERIMENTAL (env var) + /// 5 - FORC_EXPERIMENTAL (env var) + pub fn new( + manifest: &HashMap, + cli_experimental: &[Feature], + cli_no_experimental: &[Feature], + ) -> Result { + let mut experimental = ExperimentalFeatures::default(); + + experimental.parse_from_package_manifest(manifest)?; + + for f in cli_no_experimental { + experimental.set_enabled(*f, false); + } + + for f in cli_experimental { + experimental.set_enabled(*f, true); + } + + experimental.parse_from_environment_variables()?; + + Ok(experimental) + } +} + +features! { + new_encoding = true, + "https://github.com/FuelLabs/sway/issues/5727", +} + +#[derive(Clone, Debug, Default, Parser)] +pub struct CliFields { + /// Comma separated list of all experimental features that will be enabled + #[clap(long, value_delimiter = ',')] + pub experimental: Vec, + + /// Comma separated list of all experimental features that will be disabled + #[clap(long, value_delimiter = ',')] + pub no_experimental: Vec, +} + +#[derive(Debug)] +pub enum Error { + ParseError(String), + UnknownFeature(String), +} + +impl std::fmt::Display for Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Error::ParseError(feature) => f.write_fmt(format_args!( + "Experimental feature \"{feature}\" cannot be parsed." + )), + Error::UnknownFeature(feature) => { + f.write_fmt(format_args!("Unknown experimental feature: \"{feature}\".")) + } + } + } +} + +impl ExperimentalFeatures { + pub fn parse_from_package_manifest( + &mut self, + experimental: &std::collections::HashMap, + ) -> Result<(), Error> { + for (feature, enabled) in experimental { + self.set_enabled_by_name(feature, *enabled)?; + } + Ok(()) + } + + /// Enable and disable features using comma separated feature names from + /// environment variables "FORC_EXPERIMENTAL" and "FORC_NO_EXPERIMENTAL". + pub fn parse_from_environment_variables(&mut self) -> Result<(), Error> { + if let Ok(features) = std::env::var("FORC_NO_EXPERIMENTAL") { + self.parse_comma_separated_list(&features, false)?; + } + + if let Ok(features) = std::env::var("FORC_EXPERIMENTAL") { + self.parse_comma_separated_list(&features, true)?; + } + + Ok(()) + } + + pub fn parse_comma_separated_list( + &mut self, + features: impl AsRef, + enabled: bool, + ) -> Result<(), Error> { + for feature in features.as_ref().split(',') { + self.set_enabled_by_name(feature, enabled)?; + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + struct RollbackEnvVar(String, Option); + + impl RollbackEnvVar { + pub fn new(name: &str) -> Self { + let old = std::env::var(name).ok(); + RollbackEnvVar(name.to_string(), old) + } + } + + impl Drop for RollbackEnvVar { + fn drop(&mut self) { + if let Some(old) = self.1.take() { + std::env::set_var(&self.0, old); + } + } + } + + #[test] + fn ok_parse_experimental_features() { + let _old = RollbackEnvVar::new("FORC_EXPERIMENTAL"); + let _old = RollbackEnvVar::new("FORC_NO_EXPERIMENTAL"); + + let mut features = ExperimentalFeatures { + new_encoding: false, + }; + + std::env::set_var("FORC_EXPERIMENTAL", "new_encoding"); + std::env::set_var("FORC_NO_EXPERIMENTAL", ""); + assert!(!features.new_encoding); + let _ = features.parse_from_environment_variables(); + assert!(features.new_encoding); + + std::env::set_var("FORC_EXPERIMENTAL", ""); + std::env::set_var("FORC_NO_EXPERIMENTAL", "new_encoding"); + assert!(features.new_encoding); + let _ = features.parse_from_environment_variables(); + assert!(!features.new_encoding); + } +} diff --git a/sway-ir/Cargo.toml b/sway-ir/Cargo.toml index a3c4b58f722..331302af16b 100644 --- a/sway-ir/Cargo.toml +++ b/sway-ir/Cargo.toml @@ -19,6 +19,7 @@ peg.workspace = true prettydiff.workspace = true rustc-hash.workspace = true slotmap.workspace = true +sway-features.workspace = true sway-ir-macros.workspace = true sway-types.workspace = true sway-utils.workspace = true diff --git a/sway-ir/src/bin/opt.rs b/sway-ir/src/bin/opt.rs index a687f3a2cd5..4615849a55e 100644 --- a/sway-ir/src/bin/opt.rs +++ b/sway-ir/src/bin/opt.rs @@ -4,9 +4,10 @@ use std::{ }; use anyhow::anyhow; +use sway_features::ExperimentalFeatures; use sway_ir::{ - insert_after_each, register_known_passes, ExperimentalFlags, PassGroup, PassManager, - MODULE_PRINTER_NAME, MODULE_VERIFIER_NAME, + insert_after_each, register_known_passes, PassGroup, PassManager, MODULE_PRINTER_NAME, + MODULE_VERIFIER_NAME, }; use sway_types::SourceEngine; @@ -26,13 +27,8 @@ fn main() -> Result<(), anyhow::Error> { let source_engine = SourceEngine::default(); // Parse it. XXX Improve this error message too. - let mut ir = sway_ir::parser::parse( - &input_str, - &source_engine, - ExperimentalFlags { - new_encoding: false, - }, - )?; + let mut ir = + sway_ir::parser::parse(&input_str, &source_engine, ExperimentalFeatures::default())?; // Perform optimisation passes in order. let mut passes = PassGroup::default(); diff --git a/sway-ir/src/context.rs b/sway-ir/src/context.rs index aae892bff79..a9e707336fd 100644 --- a/sway-ir/src/context.rs +++ b/sway-ir/src/context.rs @@ -8,6 +8,7 @@ use rustc_hash::FxHashMap; use slotmap::{DefaultKey, SlotMap}; +use sway_features::ExperimentalFeatures; use sway_types::SourceEngine; use crate::{ @@ -40,16 +41,11 @@ pub struct Context<'eng> { next_unique_sym_tag: u64, - pub experimental: ExperimentalFlags, -} - -#[derive(Copy, Clone)] -pub struct ExperimentalFlags { - pub new_encoding: bool, + pub experimental: ExperimentalFeatures, } impl<'eng> Context<'eng> { - pub fn new(source_engine: &'eng SourceEngine, experimental: ExperimentalFlags) -> Self { + pub fn new(source_engine: &'eng SourceEngine, experimental: ExperimentalFeatures) -> Self { let mut def = Self { source_engine, modules: Default::default(), diff --git a/sway-ir/src/irtype.rs b/sway-ir/src/irtype.rs index 1211c1da6fe..1771ca954bc 100644 --- a/sway-ir/src/irtype.rs +++ b/sway-ir/src/irtype.rs @@ -691,8 +691,9 @@ mod tests { /// Unit tests in this module document and assert decisions on memory layout. mod memory_layout { use super::*; - use crate::{Context, ExperimentalFlags}; + use crate::Context; use once_cell::sync::Lazy; + use sway_features::ExperimentalFeatures; use sway_types::SourceEngine; #[test] @@ -975,12 +976,7 @@ mod tests { static SOURCE_ENGINE: Lazy = Lazy::new(SourceEngine::default); fn create_context() -> Context<'static> { - Context::new( - &SOURCE_ENGINE, - ExperimentalFlags { - new_encoding: false, - }, - ) + Context::new(&SOURCE_ENGINE, ExperimentalFeatures::default()) } /// Creates sample types that are not aggregates and do not point to diff --git a/sway-ir/src/optimize.rs b/sway-ir/src/optimize.rs index 564002fff13..299c51c0332 100644 --- a/sway-ir/src/optimize.rs +++ b/sway-ir/src/optimize.rs @@ -46,7 +46,8 @@ mod target_fuel; #[cfg(test)] pub mod tests { - use crate::{ExperimentalFlags, PassGroup, PassManager}; + use crate::{PassGroup, PassManager}; + use sway_features::ExperimentalFeatures; use sway_types::SourceEngine; /// This function parses the IR text representation and run the specified optimizers passes. @@ -85,9 +86,7 @@ pub mod tests { " ), &source_engine, - ExperimentalFlags { - new_encoding: false, - }, + ExperimentalFeatures::default(), ) .unwrap(); diff --git a/sway-ir/src/parser.rs b/sway-ir/src/parser.rs index bb77348cf81..fb577d05e15 100644 --- a/sway-ir/src/parser.rs +++ b/sway-ir/src/parser.rs @@ -1,15 +1,16 @@ //! A parser for the printed IR, useful mostly for testing. +use sway_features::ExperimentalFeatures; use sway_types::SourceEngine; -use crate::{context::Context, error::IrError, ExperimentalFlags}; +use crate::{context::Context, error::IrError}; // ------------------------------------------------------------------------------------------------- /// Parse a string produced by [`crate::printer::to_string`] into a new [`Context`]. pub fn parse<'eng>( input: &str, source_engine: &'eng SourceEngine, - experimental: ExperimentalFlags, + experimental: ExperimentalFeatures, ) -> Result, IrError> { let irmod = ir_builder::parser::ir_descrs(input).map_err(|err| { let found = if input.len() - err.location.offset <= 20 { @@ -26,6 +27,7 @@ pub fn parse<'eng>( mod ir_builder { use slotmap::KeyData; + use sway_features::ExperimentalFeatures; use sway_types::{ident::Ident, span::Span, u256::U256, SourceEngine}; type MdIdxRef = u64; @@ -686,8 +688,7 @@ mod ir_builder { metadata::{MetadataIndex, Metadatum}, module::{Kind, Module}, value::Value, - BinaryOpKind, BlockArgument, ConfigContent, ExperimentalFlags, Instruction, UnaryOpKind, - B256, + BinaryOpKind, BlockArgument, ConfigContent, Instruction, UnaryOpKind, B256, }; #[derive(Debug)] @@ -961,7 +962,7 @@ mod ir_builder { pub(super) fn build_context( ir_ast_mod: IrAstModule, source_engine: &SourceEngine, - experimental: ExperimentalFlags, + experimental: ExperimentalFeatures, ) -> Result { let mut ctx = Context::new(source_engine, experimental); let md_map = build_metadata_map(&mut ctx, ir_ast_mod.metadata); diff --git a/sway-ir/tests/tests.rs b/sway-ir/tests/tests.rs index 197d67d400e..743b3523977 100644 --- a/sway-ir/tests/tests.rs +++ b/sway-ir/tests/tests.rs @@ -1,15 +1,16 @@ use std::path::PathBuf; use itertools::Itertools; +use sway_features::ExperimentalFeatures; use sway_ir::{ create_arg_demotion_pass, create_ccp_pass, create_const_demotion_pass, create_const_folding_pass, create_cse_pass, create_dce_pass, create_dom_fronts_pass, create_dominators_pass, create_escaped_symbols_pass, create_mem2reg_pass, create_memcpyopt_pass, create_misc_demotion_pass, create_postorder_pass, create_ret_demotion_pass, create_simplify_cfg_pass, metadata_to_inline, optimize as opt, - register_known_passes, Context, ExperimentalFlags, Function, IrError, PassGroup, PassManager, - Value, DCE_NAME, FN_DCE_NAME, FN_DEDUP_DEBUG_PROFILE_NAME, FN_DEDUP_RELEASE_PROFILE_NAME, - MEM2REG_NAME, SROA_NAME, + register_known_passes, Context, Function, IrError, PassGroup, PassManager, Value, DCE_NAME, + FN_DCE_NAME, FN_DEDUP_DEBUG_PROFILE_NAME, FN_DEDUP_RELEASE_PROFILE_NAME, MEM2REG_NAME, + SROA_NAME, }; use sway_types::SourceEngine; @@ -26,17 +27,16 @@ fn run_tests bool>(sub_dir: &str, opt_fn: F) { let input_bytes = std::fs::read(&path).unwrap(); let input = String::from_utf8_lossy(&input_bytes); - let mut ir = sway_ir::parser::parse( - &input, - &source_engine, - ExperimentalFlags { - new_encoding: false, + let experimental = ExperimentalFeatures { + new_encoding: false, + }; + + let mut ir = sway_ir::parser::parse(&input, &source_engine, experimental).unwrap_or_else( + |parse_err| { + println!("{}: {parse_err}", path.display()); + panic!() }, - ) - .unwrap_or_else(|parse_err| { - println!("{}: {parse_err}", path.display()); - panic!() - }); + ); let first_line = input.split('\n').next().unwrap(); @@ -122,13 +122,8 @@ fn run_ir_verifier_tests(sub_dir: &str) { } }; - let parse_result = sway_ir::parser::parse( - &input, - &source_engine, - ExperimentalFlags { - new_encoding: false, - }, - ); + let parse_result = + sway_ir::parser::parse(&input, &source_engine, ExperimentalFeatures::default()); match parse_result { Ok(_) => { diff --git a/sway-lsp/Cargo.toml b/sway-lsp/Cargo.toml index f9648768eb5..20e1f3f646c 100644 --- a/sway-lsp/Cargo.toml +++ b/sway-lsp/Cargo.toml @@ -30,6 +30,7 @@ serde_json.workspace = true sway-ast.workspace = true sway-core.workspace = true sway-error.workspace = true +sway-features.workspace = true sway-parse.workspace = true sway-types.workspace = true sway-utils.workspace = true diff --git a/sway-lsp/benches/lsp_benchmarks/compile.rs b/sway-lsp/benches/lsp_benchmarks/compile.rs index 315d562b55a..7668d69f246 100644 --- a/sway-lsp/benches/lsp_benchmarks/compile.rs +++ b/sway-lsp/benches/lsp_benchmarks/compile.rs @@ -1,6 +1,6 @@ use codspeed_criterion_compat::{black_box, criterion_group, Criterion}; use std::sync::Arc; -use sway_core::{Engines, ExperimentalFlags}; +use sway_core::Engines; use sway_lsp::core::session; use tokio::runtime::Runtime; @@ -21,25 +21,19 @@ fn benchmarks(c: &mut Criterion) { file_versions: Default::default(), }); - let experimental = ExperimentalFlags { - new_encoding: false, - }; - c.bench_function("compile", |b| { b.iter(|| { let engines = Engines::default(); let _ = black_box( - session::compile(&build_plan, &engines, None, lsp_mode.as_ref(), experimental) - .unwrap(), + session::compile(&build_plan, &engines, None, lsp_mode.as_ref()).unwrap(), ); }) }); c.bench_function("traverse", |b| { let engines = Engines::default(); - let results = black_box( - session::compile(&build_plan, &engines, None, lsp_mode.as_ref(), experimental).unwrap(), - ); + let results = + black_box(session::compile(&build_plan, &engines, None, lsp_mode.as_ref()).unwrap()); let session = Arc::new(session::Session::new()); b.iter(|| { let _ = black_box( @@ -60,8 +54,7 @@ fn benchmarks(c: &mut Criterion) { b.iter(|| { for _ in 0..NUM_DID_CHANGE_ITERATIONS { let _ = black_box( - session::compile(&build_plan, &engines, None, lsp_mode.as_ref(), experimental) - .unwrap(), + session::compile(&build_plan, &engines, None, lsp_mode.as_ref()).unwrap(), ); } }) diff --git a/sway-lsp/benches/lsp_benchmarks/mod.rs b/sway-lsp/benches/lsp_benchmarks/mod.rs index d8847eda1c3..5e5ed0c93cf 100644 --- a/sway-lsp/benches/lsp_benchmarks/mod.rs +++ b/sway-lsp/benches/lsp_benchmarks/mod.rs @@ -4,16 +4,12 @@ pub mod token_map; use lsp_types::Url; use std::{path::PathBuf, sync::Arc}; -use sway_core::ExperimentalFlags; use sway_lsp::core::{ document::Documents, session::{self, Session}, }; pub async fn compile_test_project() -> (Url, Arc, Documents) { - let experimental = ExperimentalFlags { - new_encoding: false, - }; let session = Arc::new(Session::new()); let documents = Documents::new(); let lsp_mode = Some(sway_core::LspConfig { @@ -30,7 +26,6 @@ pub async fn compile_test_project() -> (Url, Arc, Documents) { None, lsp_mode, session.clone(), - experimental, ) .unwrap(); (uri, session, documents) diff --git a/sway-lsp/src/core/session.rs b/sway-lsp/src/core/session.rs index c55869d5f66..fe5d59a8c9f 100644 --- a/sway-lsp/src/core/session.rs +++ b/sway-lsp/src/core/session.rs @@ -298,7 +298,6 @@ pub fn compile( engines: &Engines, retrigger_compilation: Option>, lsp_mode: Option<&LspConfig>, - experimental: sway_core::ExperimentalFlags, ) -> Result, Handler)>, LanguageServerError> { let _p = tracing::trace_span!("compile").entered(); pkg::check( @@ -309,7 +308,8 @@ pub fn compile( true, engines, retrigger_compilation, - experimental, + &[], + &[sway_features::Feature::NewEncoding], ) .map_err(LanguageServerError::FailedToCompile) } @@ -442,7 +442,6 @@ pub fn parse_project( retrigger_compilation: Option>, lsp_mode: Option, session: Arc, - experimental: sway_core::ExperimentalFlags, ) -> Result<(), LanguageServerError> { let _p = tracing::trace_span!("parse_project").entered(); let build_plan = session @@ -454,7 +453,6 @@ pub fn parse_project( engines, retrigger_compilation, lsp_mode.as_ref(), - experimental, )?; // Check if the last result is None or if results is empty, indicating an error occurred in the compiler. @@ -716,17 +714,8 @@ mod tests { let uri = get_url(&dir); let engines = Engines::default(); let session = Arc::new(Session::new()); - let result = parse_project( - &uri, - &engines, - None, - None, - session, - sway_core::ExperimentalFlags { - new_encoding: false, - }, - ) - .expect_err("expected ManifestFileNotFound"); + let result = parse_project(&uri, &engines, None, None, session) + .expect_err("expected ManifestFileNotFound"); assert!(matches!( result, LanguageServerError::DocumentError( diff --git a/sway-lsp/src/handlers/notification.rs b/sway-lsp/src/handlers/notification.rs index 7c7dfddf7cc..5298869216d 100644 --- a/sway-lsp/src/handlers/notification.rs +++ b/sway-lsp/src/handlers/notification.rs @@ -39,7 +39,6 @@ pub async fn handle_did_open_text_document( file_versions: BTreeMap::new(), })); state.is_compiling.store(true, Ordering::SeqCst); - state.wait_for_parsing().await; state .publish_diagnostics(uri, params.text_document.uri, session) diff --git a/sway-lsp/src/server_state.rs b/sway-lsp/src/server_state.rs index 2c39abddeac..5e2a227f3f5 100644 --- a/sway-lsp/src/server_state.rs +++ b/sway-lsp/src/server_state.rs @@ -126,16 +126,17 @@ impl ServerState { let finished_compilation = self.finished_compilation.clone(); let rx = self.cb_rx.clone(); let last_compilation_state = self.last_compilation_state.clone(); - let experimental = sway_core::ExperimentalFlags { - new_encoding: false, - }; std::thread::spawn(move || { while let Ok(msg) = rx.recv() { match msg { TaskMessage::CompilationContext(ctx) => { + dbg!(); let uri = ctx.uri.as_ref().unwrap().clone(); + dbg!(); let session = ctx.session.as_ref().unwrap().clone(); + dbg!(); let mut engines_clone = session.engines.read().clone(); + dbg!(); // Perform garbage collection if enabled to manage memory usage. if ctx.gc_options.gc_enabled { @@ -150,20 +151,22 @@ impl ServerState { ); } } - + dbg!(); let lsp_mode = Some(LspConfig { optimized_build: ctx.optimized_build, file_versions: ctx.file_versions, }); + + dbg!(); // Set the is_compiling flag to true so that the wait_for_parsing function knows that we are compiling is_compiling.store(true, Ordering::SeqCst); + dbg!(); match session::parse_project( &uri, &engines_clone, Some(retrigger_compilation.clone()), lsp_mode, session.clone(), - experimental, ) { Ok(()) => { let path = uri.to_file_path().unwrap(); @@ -199,21 +202,26 @@ impl ServerState { } } Err(_err) => { + dbg!(_err); *last_compilation_state.write() = LastCompilationState::Failed; } } + dbg!(); // Reset the flags to false is_compiling.store(false, Ordering::SeqCst); retrigger_compilation.store(false, Ordering::SeqCst); + dbg!(); // Make sure there isn't any pending compilation work if rx.is_empty() { // finished compilation, notify waiters finished_compilation.notify_waiters(); } + dbg!(); } TaskMessage::Terminate => { + dbg!(); // If we receive a terminate message, we need to exit the thread return; } @@ -297,6 +305,7 @@ impl ServerState { session: Arc, ) { let diagnostics = self.diagnostics(&uri, session.clone()); + dbg!(&diagnostics); // Note: Even if the computed diagnostics vec is empty, we still have to push the empty Vec // in order to clear former diagnostics. Newly pushed diagnostics always replace previously pushed diagnostics. if let Some(client) = self.client.as_ref() { diff --git a/sway-lsp/src/traverse/parsed_tree.rs b/sway-lsp/src/traverse/parsed_tree.rs index 880915f915b..04e7750fa69 100644 --- a/sway-lsp/src/traverse/parsed_tree.rs +++ b/sway-lsp/src/traverse/parsed_tree.rs @@ -798,7 +798,6 @@ impl Parse for ParsedDeclId { if let TypeInfo::Custom { qualified_call_path, type_arguments, - root_type_id: _, } = &&*ctx .engines .te() @@ -1099,7 +1098,6 @@ fn collect_type_info_token(ctx: &ParseContext, type_info: &TypeInfo, type_span: TypeInfo::Custom { qualified_call_path, type_arguments, - root_type_id: _, } => { collect_qualified_path_root(ctx, qualified_call_path.qualified_path_root.clone()); let ident = qualified_call_path.call_path.suffix.clone(); diff --git a/sway-lsp/src/traverse/typed_tree.rs b/sway-lsp/src/traverse/typed_tree.rs index 3c6098cc43d..0c2745c6f21 100644 --- a/sway-lsp/src/traverse/typed_tree.rs +++ b/sway-lsp/src/traverse/typed_tree.rs @@ -1321,7 +1321,6 @@ fn collect_type_id( TypeInfo::Custom { type_arguments, qualified_call_path: name, - root_type_id: _, } => { collect_qualified_path_root(ctx, name.qualified_path_root.clone()); if let Some(token) = ctx diff --git a/sway-lsp/src/utils/markdown.rs b/sway-lsp/src/utils/markdown.rs index fd9056a8deb..115f6c9bba3 100644 --- a/sway-lsp/src/utils/markdown.rs +++ b/sway-lsp/src/utils/markdown.rs @@ -50,13 +50,10 @@ fn is_sway_fence(s: &str) -> bool { let mut seen_sway_tags = false; let mut seen_other_tags = false; - let tokens = s - .trim() - .split(|c| matches!(c, ',' | ' ' | '\t')) - .filter_map(|t| { - let t = t.trim(); - (!t.is_empty()).then_some(t) - }); + let tokens = s.trim().split([',', ' ', '\t']).filter_map(|t| { + let t = t.trim(); + (!t.is_empty()).then_some(t) + }); for token in tokens { match token { diff --git a/sway-types/src/constants.rs b/sway-types/src/constants.rs index 7d1cc136cef..01a3199cb1e 100644 --- a/sway-types/src/constants.rs +++ b/sway-types/src/constants.rs @@ -49,7 +49,6 @@ pub const ALLOW_DEPRECATED_NAME: &str = "deprecated"; pub const CFG_ATTRIBUTE_NAME: &str = "cfg"; pub const CFG_TARGET_ARG_NAME: &str = "target"; pub const CFG_PROGRAM_TYPE_ARG_NAME: &str = "program_type"; -pub const CFG_EXPERIMENTAL_NEW_ENCODING: &str = "experimental_new_encoding"; pub const DEPRECATED_ATTRIBUTE_NAME: &str = "deprecated"; diff --git a/swayfmt/src/comments.rs b/swayfmt/src/comments.rs index aa6b4848aaf..e891ca15666 100644 --- a/swayfmt/src/comments.rs +++ b/swayfmt/src/comments.rs @@ -103,7 +103,7 @@ pub fn write_comments( // If the already formatted code ends with some pattern and doesn't already end with a newline, // we want to add a newline here. - if formatted_code.ends_with(&['{', '}']) && !formatted_code.ends_with('\n') { + if formatted_code.ends_with(['{', '}']) && !formatted_code.ends_with('\n') { writeln!(formatted_code)?; } diff --git a/test/Cargo.toml b/test/Cargo.toml index 3854b55f458..809116ad79f 100644 --- a/test/Cargo.toml +++ b/test/Cargo.toml @@ -24,7 +24,7 @@ glob.workspace = true hex.workspace = true insta.workspace = true libtest-mimic.workspace = true -normalize-path = "0.2.1" +normalize-path.workspace = true prettydiff.workspace = true rand.workspace = true regex.workspace = true @@ -32,6 +32,7 @@ revm.workspace = true serde_json.workspace = true sway-core.workspace = true sway-error.workspace = true +sway-features.workspace = true sway-ir.workspace = true sway-types.workspace = true sway-utils.workspace = true diff --git a/test/src/e2e_vm_tests/harness.rs b/test/src/e2e_vm_tests/harness.rs index 6168b1f4236..164d442e7bf 100644 --- a/test/src/e2e_vm_tests/harness.rs +++ b/test/src/e2e_vm_tests/harness.rs @@ -1,3 +1,4 @@ +use super::RunConfig; use anyhow::{anyhow, bail, Result}; use colored::Colorize; use forc_client::{ @@ -5,9 +6,7 @@ use forc_client::{ op::{deploy, run, DeployedPackage}, NodeTarget, }; -use forc_pkg::{ - manifest::build_profile::ExperimentalFlags, BuildProfile, Built, BuiltPackage, PrintOpts, -}; +use forc_pkg::{BuildProfile, Built, BuiltPackage, PrintOpts}; use fuel_tx::TransactionBuilder; use fuel_vm::fuel_tx::{self, consensus_parameters::ConsensusParametersV1}; use fuel_vm::interpreter::Interpreter; @@ -20,8 +19,6 @@ use regex::{Captures, Regex}; use std::{fs, io::Read, path::PathBuf, str::FromStr}; use sway_core::{asm_generation::ProgramABI, BuildTarget}; -use super::RunConfig; - pub const NODE_URL: &str = "http://127.0.0.1:4000"; pub const SECRET_KEY: &str = "de97d8624a438121b86a1956544bd72ed68cd69f2c99555b08b1e8c51ffd511c"; @@ -82,7 +79,7 @@ pub(crate) async fn deploy_contract(file_name: &str, run_config: &RunConfig) -> true => BuildProfile::RELEASE.to_string(), false => BuildProfile::DEBUG.to_string(), }, - no_encoding_v1: !run_config.experimental.new_encoding, + experimental: run_config.experimental.clone(), ..Default::default() }) .await?; @@ -132,7 +129,7 @@ pub(crate) async fn runs_on_node( }, contract: Some(contracts), signing_key: Some(SecretKey::from_str(SECRET_KEY).unwrap()), - no_encoding_v1: !run_config.experimental.new_encoding, + experimental: run_config.experimental.clone(), ..Default::default() }; run(command).await.map(|ran_scripts| { @@ -261,6 +258,7 @@ pub(crate) fn runs_in_vm( /// Returns a tuple with the result of the compilation, as well as the output. pub(crate) async fn compile_to_bytes(file_name: &str, run_config: &RunConfig) -> Result { println!("Compiling {} ...", file_name.bold()); + let manifest_dir = env!("CARGO_MANIFEST_DIR"); let build_opts = forc_pkg::BuildOpts { build_target: run_config.build_target, @@ -284,9 +282,8 @@ pub(crate) async fn compile_to_bytes(file_name: &str, run_config: &RunConfig) -> terse: false, ..Default::default() }, - experimental: ExperimentalFlags { - new_encoding: run_config.experimental.new_encoding, - }, + experimental: run_config.experimental.experimental.clone(), + no_experimental: run_config.experimental.no_experimental.clone(), ..Default::default() }; match std::panic::catch_unwind(|| forc_pkg::build_with_options(&build_opts)) { @@ -329,9 +326,8 @@ pub(crate) async fn compile_and_run_unit_tests( terse: !(capture_output || run_config.verbose), ..Default::default() }, - experimental: ExperimentalFlags { - new_encoding: run_config.experimental.new_encoding, - }, + experimental: run_config.experimental.experimental.clone(), + no_experimental: run_config.experimental.no_experimental.clone(), ..Default::default() }) }) { @@ -355,19 +351,40 @@ pub(crate) fn test_json_abi( built_package: &BuiltPackage, experimental_new_encoding: bool, update_output_files: bool, + suffix: &Option, + has_experimental_field: bool, ) -> Result<()> { emit_json_abi(file_name, built_package)?; let manifest_dir = env!("CARGO_MANIFEST_DIR"); - let oracle_path = if experimental_new_encoding { - format!( - "{}/src/e2e_vm_tests/test_programs/{}/{}", - manifest_dir, file_name, "json_abi_oracle_new_encoding.json" - ) - } else { - format!( - "{}/src/e2e_vm_tests/test_programs/{}/{}", - manifest_dir, file_name, "json_abi_oracle.json" - ) + + let oracle_path = match (has_experimental_field, experimental_new_encoding) { + (true, _) => { + format!( + "{}/src/e2e_vm_tests/test_programs/{}/json_abi_oracle.{}json", + manifest_dir, + file_name, + suffix + .as_ref() + .unwrap() + .strip_prefix("test") + .unwrap() + .strip_suffix("toml") + .unwrap() + .trim_start_matches('.') + ) + } + (false, true) => { + format!( + "{}/src/e2e_vm_tests/test_programs/{}/{}", + manifest_dir, file_name, "json_abi_oracle_new_encoding.json" + ) + } + (false, false) => { + format!( + "{}/src/e2e_vm_tests/test_programs/{}/{}", + manifest_dir, file_name, "json_abi_oracle.json" + ) + } }; let output_path = format!( diff --git a/test/src/e2e_vm_tests/mod.rs b/test/src/e2e_vm_tests/mod.rs index 7eaca4d658b..62dc360fea1 100644 --- a/test/src/e2e_vm_tests/mod.rs +++ b/test/src/e2e_vm_tests/mod.rs @@ -27,6 +27,7 @@ use std::{ sync::Arc, }; use sway_core::BuildTarget; +use sway_features::{CliFields, ExperimentalFeatures}; use tokio::sync::Mutex; use tracing::Instrument; @@ -101,6 +102,8 @@ struct TestDescription { unsupported_profiles: Vec<&'static str>, checker: FileCheck, run_config: RunConfig, + experimental: ExperimentalFeatures, + has_experimental_field: bool, } #[derive(PartialEq, Eq, Hash)] @@ -280,9 +283,16 @@ impl TestContext { run_config: &RunConfig, contract_path: String, ) -> Result { + let experimental = ExperimentalFeatures::new( + &HashMap::default(), + &run_config.experimental.experimental, + &run_config.experimental.no_experimental, + ) + .unwrap(); + let key = DeployedContractKey { contract_path: contract_path.clone(), - new_encoding: run_config.experimental.new_encoding, + new_encoding: experimental.new_encoding, }; let mut deployed_contracts = self.deployed_contracts.lock().await; @@ -298,6 +308,7 @@ impl TestContext { async fn run(&self, test: TestDescription, output: &mut String, verbose: bool) -> Result<()> { let TestDescription { name, + suffix, category, script_data, script_data_new_encoding, @@ -311,18 +322,20 @@ impl TestContext { checker, run_config, expected_decoded_test_logs, + experimental, + has_experimental_field, .. } = test; let checker = checker.build().unwrap(); - let script_data = if run_config.experimental.new_encoding { + let script_data = if !has_experimental_field && experimental.new_encoding { script_data_new_encoding } else { script_data }; - let expected_result = if run_config.experimental.new_encoding { + let expected_result = if !has_experimental_field && experimental.new_encoding { expected_result_new_encoding } else { expected_result @@ -438,11 +451,14 @@ impl TestContext { harness::test_json_abi( &name, &compiled, - run_config.experimental.new_encoding, + experimental.new_encoding, run_config.update_output_files, + &suffix, + has_experimental_field, ) }) .await; + output.push_str(&out); result?; } @@ -484,8 +500,10 @@ impl TestContext { harness::test_json_abi( name, built_pkg, - run_config.experimental.new_encoding, + experimental.new_encoding, run_config.update_output_files, + &suffix, + has_experimental_field, ) }) .await; @@ -686,7 +704,7 @@ impl TestContext { pub async fn run(filter_config: &FilterConfig, run_config: &RunConfig) -> Result<()> { // Discover tests - let mut tests = discover_test_configs(run_config)?; + let mut tests = discover_test_tomls(run_config)?; let total_number_of_tests = tests.len(); // Filter tests @@ -882,36 +900,23 @@ fn exclude_tests_dependency(t: &TestDescription, dep: &str) -> bool { } } -fn discover_test_configs(run_config: &RunConfig) -> Result> { - fn recursive_search( - path: &Path, - run_config: &RunConfig, - configs: &mut Vec, - ) -> Result<()> { - let wrap_err = |e| { - let relative_path = path - .iter() - .skip_while(|part| part.to_string_lossy() != "test_programs") - .skip(1) - .collect::(); - anyhow!("{}: {}", relative_path.display(), e) - }; - if path.is_dir() { - for entry in std::fs::read_dir(path).unwrap() { - recursive_search(&entry.unwrap().path(), run_config, configs)?; - } - } else if path.is_file() && path.file_name().map(|f| f == "test.toml").unwrap_or(false) { - configs.push(parse_test_toml(path, run_config).map_err(wrap_err)?); - } - Ok(()) - } +fn discover_test_tomls(run_config: &RunConfig) -> Result> { + let mut descriptions = vec![]; - let manifest_dir = env!("CARGO_MANIFEST_DIR"); - let tests_root_dir = format!("{manifest_dir}/src/e2e_vm_tests/test_programs"); + let pattern = format!( + "{}/src/e2e_vm_tests/test_programs/**/test*.toml", + env!("CARGO_MANIFEST_DIR") + ); + + for entry in glob::glob(&pattern) + .expect("Failed to read glob pattern") + .flatten() + { + let t = parse_test_toml(&entry, run_config)?; + descriptions.push(t); + } - let mut configs = Vec::new(); - recursive_search(&PathBuf::from(tests_root_dir), run_config, &mut configs)?; - Ok(configs) + Ok(descriptions) } /// This functions gets passed the previously built FileCheck-based file checker, @@ -936,22 +941,78 @@ fn parse_test_toml(path: &Path, run_config: &RunConfig) -> Result()?; + let mut toml_content = toml_content_str.parse::()?; if !toml_content.is_table() { bail!("Malformed test description."); } + // use test.toml as base if this test has a suffix + { + let toml_content_map = toml_content.as_table_mut().unwrap(); + if path.file_name().and_then(|x| x.to_str()) != Some("test.toml") { + let base_test_toml = path.parent().unwrap().join("test.toml"); + let base_test_toml = std::fs::read_to_string(base_test_toml)?; + let base_toml_content = base_test_toml.parse::()?; + match base_toml_content { + toml::Value::Table(map) => { + for (k, v) in map { + let _ = toml_content_map.entry(&k).or_insert(v); + } + } + _ => bail!("Malformed base test description (see test.toml)."), + } + }; + } + + let mut run_config = run_config.clone(); + + // To keep the current test.toml compatible we check if a field named "experimental" exists + // or not. If it does not, we keep the current behaviour. + // If it does, we ignore the experimental flags from the CLI and use the one from the toml file. + // TODO: this backwards compatibility can be removed after all tests migrate to new version + let (has_experimental_field, experimental) = + if let Some(toml_experimental) = toml_content.get("experimental") { + run_config.experimental = CliFields::default(); + + let mut experimental = ExperimentalFeatures::default(); + for (k, v) in toml_experimental.as_table().unwrap() { + let v = v.as_bool().unwrap(); + experimental.set_enabled_by_name(k, v).unwrap(); + + if v { + run_config + .experimental + .experimental + .push(k.parse().unwrap()); + } else { + run_config + .experimental + .no_experimental + .push(k.parse().unwrap()); + } + } + (true, experimental) + } else { + let mut experimental = ExperimentalFeatures::default(); + for f in &run_config.experimental.no_experimental { + experimental.set_enabled(*f, false); + } + for f in &run_config.experimental.experimental { + experimental.set_enabled(*f, true); + } + (false, experimental) + }; + // if new encoding is on, allow a "category_new_encoding" // for tests that should have different categories - let category = if run_config.experimental.new_encoding { + let category = if !has_experimental_field && experimental.new_encoding { toml_content .get("category_new_encoding") .or_else(|| toml_content.get("category")) } else { toml_content.get("category") }; - let category = category .ok_or_else(|| anyhow!("Missing mandatory 'category' entry.")) .and_then(|category_val| match category_val.as_str() { @@ -991,7 +1052,8 @@ fn parse_test_toml(path: &Path, run_config: &RunConfig) -> Result Result { - bail!("Expected 'script_data' to be a hex string."); + bail!("Expected 'script_data_new_encoding' to be a hex string."); } _ => None, }; - (script_data, script_data_new_encoding) + ( + script_data, + if has_experimental_field { + None + } else { + script_data_new_encoding + }, + ) } TestCategory::Compiles | TestCategory::FailsToCompile @@ -1077,7 +1146,11 @@ fn parse_test_toml(path: &Path, run_config: &RunConfig) -> Result Some(value), + (TestCategory::Runs | TestCategory::RunsWithContract, Some(value)) + if !has_experimental_field => + { + Some(value) + } _ => None, }; @@ -1158,11 +1231,7 @@ fn parse_test_toml(path: &Path, run_config: &RunConfig) -> Result Result forc build --path test/src/e2e_vm_tests/test_programs/should_fail/invalid_cfg_arg +exit status: 1 +output: + Building test/src/e2e_vm_tests/test_programs/should_fail/invalid_cfg_arg + Compiling predicate invalid_cfg_arg (test/src/e2e_vm_tests/test_programs/should_fail/invalid_cfg_arg) +warning + --> test/src/e2e_vm_tests/test_programs/should_fail/invalid_cfg_arg/src/main.sw:2:3 + | +1 | predicate; +2 | #[cfg(c)] a + | --- Unexpected attribute value: "c" for attribute: "cfg" expected value "target" or "program_type" or "experimental_new_encoding" + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_fail/invalid_cfg_arg/src/main.sw:2:11 + | +1 | predicate; +2 | #[cfg(c)] a + | ^ Expected an item. + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_fail/invalid_cfg_arg/src/main.sw:2:7 + | +1 | predicate; +2 | #[cfg(c)] a + | ^ Unexpected attribute value: "c" for attribute: "cfg" + | +____ + + Aborting due to 2 errors. +error: Failed to compile invalid_cfg_arg diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/invalid_cfg_arg/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/invalid_cfg_arg/test.toml deleted file mode 100644 index 1f48bdf0556..00000000000 --- a/test/src/e2e_vm_tests/test_programs/should_fail/invalid_cfg_arg/test.toml +++ /dev/null @@ -1,10 +0,0 @@ -category = "fail" - -# check: $()#[cfg(c)] a -# nextln: $()Unexpected attribute value: "c" for attribute: "cfg" expected value "target" or "program_type" or "experimental_new_encoding" - -# check: $()#[cfg(c)] a -# nextln: $()Expected an item. - -# check: $()#[cfg(c)] a -# nextln: $()Unexpected attribute value: "c" for attribute: "cfg" \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/forc/help/snapshot.toml b/test/src/e2e_vm_tests/test_programs/should_pass/forc/help/snapshot.toml new file mode 100644 index 00000000000..b9151f7a736 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/forc/help/snapshot.toml @@ -0,0 +1,16 @@ +cmds = [ + "forc addr2line -h", + "forc build -h", + "forc check -h", + "forc clean -h", + "forc completions -h", + "forc new -h", + "forc init -h", + "forc parse-bytecode -h", + "forc test -h", + "forc update -h", + "forc plugins -h", + "forc template -h", + "forc contract-id -h", + "forc predicate-root -h", +] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/forc/help/stdout.snap b/test/src/e2e_vm_tests/test_programs/should_pass/forc/help/stdout.snap new file mode 100644 index 00000000000..8dd4a804aae --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/forc/help/stdout.snap @@ -0,0 +1,597 @@ +--- +source: test/tests/tests.rs +--- +> forc addr2line -h +exit status: 0 +output: +Show location and context of an opcode address in its source file + +Usage: forc addr2line [OPTIONS] --sourcemap-path --opcode-index + +Options: + -S, --search-dir Where to search for the project root [default: .] + -g, --sourcemap-path Source file mapping in JSON format + -c, --context How many lines of context to show [default: 2] + -i, --opcode-index Opcode index + -v, --verbose... Use verbose output + -s, --silent Silence all output + -L, --log-level Set the log level + -h, --help Print help + +> forc build -h +exit status: 0 +output: +Compile the current or target project + +Usage: forc build [OPTIONS] + +Options: + -p, --path + Path to the project + --offline + Offline mode + -t, --terse + Terse mode + --output-directory + The directory in which Forc output artifacts are placed + --locked + Requires that the Forc.lock file is up-to-date + --ipfs-node + The IPFS node to use for fetching IPFS sources + --ast + Print the generated Sway AST (Abstract Syntax Tree) + --dca-graph + Print the computed Sway DCA (Dead Code Analysis) graph + --dca-graph-url-format + URL format to be used in the generated DCA graph .dot file. + --asm ... + Print the generated ASM (assembler). [possible values: virtual, allocated, abstract, final, all] + --bytecode + Print the bytecode + --ir ... + Print the generated Sway IR (Intermediate Representation). [possible values: initial, final, all, modified, inline, simplify-cfg, sroa, dce, fn-dce, fn-dedup-release, fn-dedup-debug, mem2reg, memcpyopt, const-folding, arg-demotion, const-demotion, ret-demotion, misc-demotion] + --time-phases + Output the time elapsed over each part of the compilation process + --reverse-order + Output build errors and warnings in reverse order + --metrics-outfile + Output compilation metrics into the specified file + -v, --verbose... + Use verbose output + --json-abi + Minify JSON ABI files + -s, --silent + Silence all output + --json-storage-slots + Minify JSON storage slot files + -L, --log-level + Set the log level + -o, --output-bin + Create a binary file at the provided path representing the final bytecode + -g, --output-debug + Create a file at the provided path containing debug information + --build-profile + The name of the build profile to use [default: debug] + --release + Use the release build profile + --error-on-warnings + Treat warnings as errors + --build-target + Build target to use for code generation [default: fuel] [possible values: fuel, evm] + --tests + Also build all tests within the project + --experimental + Comma separated list of all experimental features that will be enabled [possible values: new_encoding] + --no-experimental + Comma separated list of all experimental features that will be disabled [possible values: new_encoding] + -h, --help + Print help (see more with '--help') + -V, --version + Print version + +EXAMPLES: + # Compile the current projectx + forc build + + # Compile the current project from a different path + forc build --path + + # Compile the current project without updating dependencies + forc build --path --locked + +> forc check -h +exit status: 0 +output: +Check the current or target project and all of its dependencies for errors + +Usage: forc check [OPTIONS] [BUILD_TARGET] + +Arguments: + [BUILD_TARGET] Build target to use for code generation [default: fuel] [possible values: fuel, evm] + +Options: + -p, --path + Path to the project, if not specified, current working directory will be used + --offline + Offline mode, prevents Forc from using the network when managing dependencies. Meaning it will only try to use previously downloaded dependencies + --locked + Requires that the Forc.lock file is up-to-date. If the lock file is missing, or it needs to be updated, Forc will exit with an error + -t, --terse + Terse mode. Limited warning and error output + --disable-tests + Disable checking unit tests + --ipfs-node + The IPFS Node to use for fetching IPFS sources + --experimental + Comma separated list of all experimental features that will be enabled [possible values: new_encoding] + --no-experimental + Comma separated list of all experimental features that will be disabled [possible values: new_encoding] + -v, --verbose... + Use verbose output + -s, --silent + Silence all output + -L, --log-level + Set the log level + -h, --help + Print help (see more with '--help') + -V, --version + Print version + +EXAMPLES: + # Check the current project + forc check + + # Check the current project with a different path + forc check --path + + # Check the current project without updating dependencies + forc check --locked + +> forc clean -h +exit status: 0 +output: +Removes the default forc compiler output artifact directory, i.e. `/out` + +Usage: forc clean [OPTIONS] + +Options: + -p, --path Path to the project, if not specified, current working directory will be used + -v, --verbose... Use verbose output + -s, --silent Silence all output + -L, --log-level Set the log level + -h, --help Print help + -V, --version Print version + +EXAMPLES: + # Clean project + forc clean + + # Clean project with a custom path + forc clean --path + +> forc completions -h +exit status: 0 +output: +Generate tab-completion scripts for your shell + +Usage: forc completions [OPTIONS] --target + +Options: + -T, --target Specify shell to enable tab-completion for [possible values: bash, elvish, fish, power-shell, zsh, fig] + -v, --verbose... Use verbose output + -s, --silent Silence all output + -L, --log-level Set the log level + -h, --help Print help (see more with '--help') + +> forc new -h +exit status: 0 +output: +Create a new Forc project at `` + +Usage: forc new [OPTIONS] + +Arguments: + The path at which the project directory will be created + +Options: + --contract The default program type. Excluding all flags or adding this flag creates a basic contract program + --script Adding this flag creates an empty script program + --predicate Adding this flag creates an empty predicate program + --library Adding this flag creates an empty library program + --workspace Adding this flag creates an empty workspace + --name Set the package name. Defaults to the directory name + -v, --verbose... Use verbose output + -s, --silent Silence all output + -L, --log-level Set the log level + -h, --help Print help + -V, --version Print version + +EXAMPLES: + # Create a new project + forc new --contract --name my_project + + # Create a new workspace + forc new --workspace --name my_workspace + + # Create a new Forc project with a predicate + forc new --predicate + + # Create a new Forc library project + forc new --library + +> forc init -h +exit status: 0 +output: +Create a new Forc project in an existing directory + +Usage: forc init [OPTIONS] + +Options: + --path The directory in which the forc project will be initialized + --contract The default program type, excluding all flags or adding this flag creates a basic contract program + --script Create a package with a script target (src/main.sw) + --predicate Create a package with a predicate target (src/predicate.rs) + --library Create a package with a library target (src/lib.sw) + --workspace Adding this flag creates an empty workspace + --name Set the package name. Defaults to the directory name + -v, --verbose... Use verbose output + -s, --silent Silence all output + -L, --log-level Set the log level + -h, --help Print help + -V, --version Print version + +EXAMPLES: + # Initialize a new Forc project + forc init --path + + # Initialize a new Forc project as workspace + forc init --path --workspace + + # Initialize a new Forc project with a predicate + forc init --path --predicate + + # Initialize a new Forc library project + forc init --path --library + +> forc parse-bytecode -h +exit status: 0 +output: +Parse bytecode file into a debug format + +Usage: forc parse-bytecode [OPTIONS] + +Arguments: + + +Options: + -v, --verbose... Use verbose output + -s, --silent Silence all output + -L, --log-level Set the log level + -h, --help Print help + -V, --version Print version + +EXAMPLES: + # Parse bytecode + forc parse-bytecode + +> forc test -h +exit status: 0 +output: +Run the Sway unit tests for the current project + +Usage: forc test [OPTIONS] [FILTER] + +Arguments: + [FILTER] When specified, only tests containing the given string will be executed + +Options: + -p, --path + Path to the project + --offline + Offline mode + -t, --terse + Terse mode + --output-directory + The directory in which Forc output artifacts are placed + --locked + Requires that the Forc.lock file is up-to-date + --ipfs-node + The IPFS node to use for fetching IPFS sources + --ast + Print the generated Sway AST (Abstract Syntax Tree) + --dca-graph + Print the computed Sway DCA (Dead Code Analysis) graph + --dca-graph-url-format + URL format to be used in the generated DCA graph .dot file. + --asm ... + Print the generated ASM (assembler). [possible values: virtual, allocated, abstract, final, all] + --bytecode + Print the bytecode + --ir ... + Print the generated Sway IR (Intermediate Representation). [possible values: initial, final, all, modified, inline, simplify-cfg, sroa, dce, fn-dce, fn-dedup-release, fn-dedup-debug, mem2reg, memcpyopt, const-folding, arg-demotion, const-demotion, ret-demotion, misc-demotion] + --time-phases + Output the time elapsed over each part of the compilation process + --reverse-order + Output build errors and warnings in reverse order + --metrics-outfile + Output compilation metrics into the specified file + -v, --verbose... + Use verbose output + --json-abi + Minify JSON ABI files + -s, --silent + Silence all output + --json-storage-slots + Minify JSON storage slot files + -L, --log-level + Set the log level + -o, --output-bin + Create a binary file at the provided path representing the final bytecode + -g, --output-debug + Create a file at the provided path containing debug information + --build-profile + The name of the build profile to use [default: debug] + --release + Use the release build profile + --error-on-warnings + Treat warnings as errors + --build-target + Build target to use for code generation [default: fuel] [possible values: fuel, evm] + --pretty + Pretty-print the logs emitted from tests + -l, --logs + Print `Log` and `LogData` receipts for tests + --raw-logs + Print the raw logs for tests + --filter-exact + When specified, only the test exactly matching the given string will be executed + --test-threads + Number of threads to utilize when running the tests. By default, this is the number of threads available in your system + --experimental + Comma separated list of all experimental features that will be enabled [possible values: new_encoding] + --no-experimental + Comma separated list of all experimental features that will be disabled [possible values: new_encoding] + -h, --help + Print help (see more with '--help') + -V, --version + Print version + +EXAMPLES: + # Run test + forc test + + # Run test with a filter + forc test $filter + + # Run test without any output + forc test --silent + + # Run test without creating or update the lock file + forc test --locked + +> forc update -h +exit status: 0 +output: +Update dependencies in the Forc dependencies directory + +Usage: forc update [OPTIONS] + +Options: + -p, --path Path to the project, if not specified, current working directory will be used + -d Dependency to be updated. If not set, all dependencies will be updated + -c, --check Checks if the dependencies have newer versions. Won't actually perform the update, will output which ones are up-to-date and outdated + --ipfs-node The IPFS Node to use for fetching IPFS sources + -v, --verbose... Use verbose output + -s, --silent Silence all output + -L, --log-level Set the log level + -h, --help Print help (see more with '--help') + -V, --version Print version + +EXAMPLES: + # Update dependencies + forc update + + # Update a specific dependency + forc update -d std + + # Check if dependencies have newer versions + forc update --check + +> forc plugins -h +exit status: 0 +output: +List all forc plugins + +Usage: forc plugins [OPTIONS] + +Options: + -p, --paths Prints the absolute path to each discovered plugin + -d, --describe Prints the long description associated with each listed plugin + -v, --verbose... Use verbose output + -s, --silent Silence all output + -L, --log-level Set the log level + -h, --help Print help (see more with '--help') + -V, --version Print version + +EXAMPLES: + # List all plugins + forc plugins + + # List all plugins with their paths + forc plugins --paths + + # List all plugins with their descriptions + forc plugins --describe + + # List all plugins with their paths and descriptions + forc plugins --paths --describe + +> forc template -h +exit status: 0 +output: +Create a new Forc project from a git template + +Usage: forc template [OPTIONS] + +Arguments: + The name of the project that will be created + +Options: + -u, --url The template url, should be a git repo [default: https://github.com/fuellabs/sway] + -t, --template-name The name of the template that needs to be fetched and used from git repo provided + -v, --verbose... Use verbose output + -s, --silent Silence all output + -L, --log-level Set the log level + -h, --help Print help + -V, --version Print version + +EXAMPLES: + # Create a new Forc project from an option template + forc template new-path --template-name option + +> forc contract-id -h +exit status: 0 +output: +Determine contract-id for a contract. For workspaces outputs all contract ids in the workspace + +Usage: forc contract-id [OPTIONS] + +Options: + -p, --path + Path to the project + --offline + Offline mode + -t, --terse + Terse mode + --output-directory + The directory in which Forc output artifacts are placed + --locked + Requires that the Forc.lock file is up-to-date + --ipfs-node + The IPFS node to use for fetching IPFS sources + --json-abi + Minify JSON ABI files + --json-storage-slots + Minify JSON storage slot files + --ast + Print the generated Sway AST (Abstract Syntax Tree) + --dca-graph + Print the computed Sway DCA (Dead Code Analysis) graph + --dca-graph-url-format + URL format to be used in the generated DCA graph .dot file. + --asm ... + Print the generated ASM (assembler). [possible values: virtual, allocated, abstract, final, all] + --bytecode + Print the bytecode + --ir ... + Print the generated Sway IR (Intermediate Representation). [possible values: initial, final, all, modified, inline, simplify-cfg, sroa, dce, fn-dce, fn-dedup-release, fn-dedup-debug, mem2reg, memcpyopt, const-folding, arg-demotion, const-demotion, ret-demotion, misc-demotion] + --time-phases + Output the time elapsed over each part of the compilation process + -v, --verbose... + Use verbose output + --reverse-order + Output build errors and warnings in reverse order + -s, --silent + Silence all output + -L, --log-level + Set the log level + --metrics-outfile + Output compilation metrics into the specified file + -o, --output-bin + Create a binary file at the provided path representing the final bytecode + -g, --output-debug + Create a file at the provided path containing debug information + --build-profile + The name of the build profile to use [default: debug] + --release + Use the release build profile + --error-on-warnings + Treat warnings as errors + --salt + Added salt used to derive the contract ID + --experimental + Comma separated list of all experimental features that will be enabled [possible values: new_encoding] + --no-experimental + Comma separated list of all experimental features that will be disabled [possible values: new_encoding] + -h, --help + Print help (see more with '--help') + -V, --version + Print version + +EXAMPLES: + # Get contract id + forc contract-id + + # Get contract id from a different path + forc contract-id --path + +> forc predicate-root -h +exit status: 0 +output: +Determine predicate-root for a predicate. For workspaces outputs all predicate roots in the workspace + +Usage: forc predicate-root [OPTIONS] + +Options: + -p, --path + Path to the project + --offline + Offline mode + -t, --terse + Terse mode + --output-directory + The directory in which Forc output artifacts are placed + --locked + Requires that the Forc.lock file is up-to-date + --ipfs-node + The IPFS node to use for fetching IPFS sources + --json-abi + Minify JSON ABI files + --json-storage-slots + Minify JSON storage slot files + --ast + Print the generated Sway AST (Abstract Syntax Tree) + --dca-graph + Print the computed Sway DCA (Dead Code Analysis) graph + --dca-graph-url-format + URL format to be used in the generated DCA graph .dot file. + --asm ... + Print the generated ASM (assembler). [possible values: virtual, allocated, abstract, final, all] + --bytecode + Print the bytecode + --ir ... + Print the generated Sway IR (Intermediate Representation). [possible values: initial, final, all, modified, inline, simplify-cfg, sroa, dce, fn-dce, fn-dedup-release, fn-dedup-debug, mem2reg, memcpyopt, const-folding, arg-demotion, const-demotion, ret-demotion, misc-demotion] + --time-phases + Output the time elapsed over each part of the compilation process + -v, --verbose... + Use verbose output + --reverse-order + Output build errors and warnings in reverse order + -s, --silent + Silence all output + -L, --log-level + Set the log level + --metrics-outfile + Output compilation metrics into the specified file + -o, --output-bin + Create a binary file at the provided path representing the final bytecode + -g, --output-debug + Create a file at the provided path containing debug information + --build-profile + The name of the build profile to use [default: debug] + --release + Use the release build profile + --error-on-warnings + Treat warnings as errors + --experimental + Comma separated list of all experimental features that will be enabled [possible values: new_encoding] + --no-experimental + Comma separated list of all experimental features that will be disabled [possible values: new_encoding] + -h, --help + Print help (see more with '--help') + -V, --version + Print version + +EXAMPLES: + # Get predicate root + forc predicate-root diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/main_args/main_args_empty/json_abi_oracle_new_encoding.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/main_args/main_args_empty/json_abi_oracle.encoding_v1.json similarity index 100% rename from test/src/e2e_vm_tests/test_programs/should_pass/language/main_args/main_args_empty/json_abi_oracle_new_encoding.json rename to test/src/e2e_vm_tests/test_programs/should_pass/language/main_args/main_args_empty/json_abi_oracle.encoding_v1.json diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/main_args/main_args_empty/json_abi_oracle.json b/test/src/e2e_vm_tests/test_programs/should_pass/language/main_args/main_args_empty/json_abi_oracle.json index ad50b55d54c..e88285b3d48 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/main_args/main_args_empty/json_abi_oracle.json +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/main_args/main_args_empty/json_abi_oracle.json @@ -1,25 +1,23 @@ { + "concreteTypes": [ + { + "concreteTypeId": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0", + "type": "u64" + } + ], "configurables": [], + "encodingVersion": "0", "functions": [ { "attributes": null, "inputs": [], "name": "main", - "output": { - "name": "", - "type": 0, - "typeArguments": null - } + "output": "1506e6f44c1d6291cdf46395a8e573276a4fa79e8ace3fc891e092ef32d1b0a0" } ], "loggedTypes": [], "messagesTypes": [], - "types": [ - { - "components": null, - "type": "u64", - "typeId": 0, - "typeParameters": null - } - ] + "metadataTypes": [], + "programType": "script", + "specVersion": "1" } \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/main_args/main_args_empty/test.encoding_v1.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/main_args/main_args_empty/test.encoding_v1.toml new file mode 100644 index 00000000000..3917453e712 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/main_args/main_args_empty/test.encoding_v1.toml @@ -0,0 +1,4 @@ +script_data = "0000000000000538 0000000000000001" +expected_result = { action = "return_data", value = "0000000000000539" } + +experimental = { new_encoding = true } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/main_args/main_args_empty/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/main_args/main_args_empty/test.toml index 1074072af31..733091ba093 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/main_args/main_args_empty/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/main_args/main_args_empty/test.toml @@ -1,9 +1,9 @@ category = "run" + # (1336, 1) script_data = "0000000000000538 0000000000000001" expected_result = { action = "return", value = 1337 } -script_data_new_encoding = "0000000000000538 0000000000000001" -expected_result_new_encoding = { action = "return_data", value = "0000000000000539" } - validate_abi = true + +experimental = { new_encoding = false } diff --git a/test/src/ir_generation/mod.rs b/test/src/ir_generation/mod.rs index 9de1ab9f2e1..8313845fe39 100644 --- a/test/src/ir_generation/mod.rs +++ b/test/src/ir_generation/mod.rs @@ -13,12 +13,14 @@ use sway_core::{ }; use sway_error::handler::Handler; +use sway_features::ExperimentalFeatures; use sway_ir::{ - create_fn_inline_pass, register_known_passes, ExperimentalFlags, PassGroup, PassManager, - ARG_DEMOTION_NAME, CONST_DEMOTION_NAME, DCE_NAME, MEMCPYOPT_NAME, MISC_DEMOTION_NAME, - RET_DEMOTION_NAME, + create_fn_inline_pass, register_known_passes, PassGroup, PassManager, ARG_DEMOTION_NAME, + CONST_DEMOTION_NAME, DCE_NAME, MEMCPYOPT_NAME, MISC_DEMOTION_NAME, RET_DEMOTION_NAME, }; +use crate::RunConfig; + enum Checker { Ir, Asm, @@ -166,11 +168,8 @@ fn pretty_print_error_report(error: &str) { pub(super) async fn run( filter_regex: Option<®ex::Regex>, verbose: bool, - mut experimental: ExperimentalFlags, + run_config: &RunConfig, ) -> Result<()> { - // TODO the way modules are built for these tests, new_encoding is not working. - experimental.new_encoding = false; - // Create new initial namespace for every test by reusing the precompiled // standard libraries. The namespace, thus its root module, must have the // name set. @@ -180,7 +179,7 @@ pub(super) async fn run( // Compile core library and reuse it when compiling tests. let engines = Engines::default(); let build_target = BuildTarget::default(); - let core_lib = compile_core(core_lib_name, build_target, &engines, experimental); + let core_lib = compile_core(core_lib_name, build_target, &engines, run_config); // Find all the tests. let all_tests = discover_test_files(); @@ -223,15 +222,17 @@ pub(super) async fn run( let test_file_name = path.file_name().unwrap().to_string_lossy().to_string(); tracing::info!("Testing {} ...", test_file_name.bold()); + let experimental = ExperimentalFeatures { + new_encoding: false, // IR tests still need encoding v1 off + }; + // Compile to AST. We need to provide a faux build config otherwise the IR will have // no span metadata. let bld_cfg = sway_core::BuildConfig::root_from_file_name_and_manifest_path( path.clone(), PathBuf::from("/"), build_target, - ).with_experimental(sway_core::ExperimentalFlags { - new_encoding: experimental.new_encoding, - }); + ); // Include unit tests in the build. let bld_cfg = bld_cfg.with_include_tests(true); @@ -247,6 +248,7 @@ pub(super) async fn run( Some(&bld_cfg), PACKAGE_NAME, None, + experimental ); let (errors, _warnings) = handler.consume(); if !errors.is_empty() { @@ -274,9 +276,7 @@ pub(super) async fn run( // Compile to IR. let include_tests = true; - let mut ir = compile_program(typed_program, include_tests, &engines, sway_core::ExperimentalFlags { - new_encoding: experimental.new_encoding, - }) + let mut ir = compile_program(typed_program, include_tests, &engines, experimental) .unwrap_or_else(|e| { use sway_types::span::Spanned; let e = e[0].clone(); @@ -372,9 +372,7 @@ pub(super) async fn run( let mut ir = sway_ir::parser::parse( &ir_output, engines.se(), - sway_ir::ExperimentalFlags { - new_encoding: experimental.new_encoding, - } + experimental, ) .unwrap_or_else(|e| panic!("{}: {e}\n{ir_output}", path.display())); @@ -469,9 +467,7 @@ pub(super) async fn run( } // Parse the IR again, and print it yet again to make sure that IR de/serialisation works. - let parsed_ir = sway_ir::parser::parse(&ir_output, engines.se(), sway_ir::ExperimentalFlags { - new_encoding: experimental.new_encoding, - }) + let parsed_ir = sway_ir::parser::parse(&ir_output, engines.se(), experimental) .unwrap_or_else(|e| panic!("{}: {e}\n{ir_output}", path.display())); let parsed_ir_output = sway_ir::printer::to_string(&parsed_ir); if ir_output != parsed_ir_output { @@ -530,7 +526,7 @@ fn compile_core( lib_name: sway_types::Ident, build_target: BuildTarget, engines: &Engines, - experimental: ExperimentalFlags, + run_config: &RunConfig, ) -> namespace::Module { let manifest_dir = env!("CARGO_MANIFEST_DIR"); let libcore_root_dir = format!("{manifest_dir}/../sway-lib-core"); @@ -543,7 +539,7 @@ fn compile_core( disable_tests: false, locked: false, ipfs_node: None, - no_encoding_v1: !experimental.new_encoding, + experimental: run_config.experimental.clone(), }; let res = match forc::test::forc_check::check(check_cmd, engines) { diff --git a/test/src/main.rs b/test/src/main.rs index 22a6c01c0e1..662e95e83f0 100644 --- a/test/src/main.rs +++ b/test/src/main.rs @@ -8,7 +8,7 @@ use clap::Parser; use forc::cli::shared::{PrintAsmCliOpt, PrintIrCliOpt}; use forc_tracing::init_tracing_subscriber; use std::str::FromStr; -use sway_core::{BuildTarget, ExperimentalFlags, PrintAsm, PrintIr}; +use sway_core::{BuildTarget, PrintAsm, PrintIr}; use tracing::Instrument; #[derive(Parser)] @@ -61,10 +61,6 @@ struct Cli { #[arg(long, visible_alias = "target")] build_target: Option, - /// Disable the "new encoding" feature - #[arg(long)] - no_encoding_v1: bool, - /// Update all output files #[arg(long)] update_output_files: bool, @@ -80,6 +76,9 @@ struct Cli { /// Print out the final bytecode, if the verbose option is on #[arg(long)] print_bytecode: bool, + + #[command(flatten)] + experimental: sway_features::CliFields, } #[derive(Debug, Clone)] @@ -100,11 +99,11 @@ pub struct RunConfig { pub locked: bool, pub verbose: bool, pub release: bool, - pub experimental: ExperimentalFlags, pub update_output_files: bool, pub print_ir: PrintIr, pub print_asm: PrintAsm, pub print_bytecode: bool, + pub experimental: sway_features::CliFields, } #[tokio::main] @@ -114,7 +113,7 @@ async fn main() -> Result<()> { // Parse args let cli = Cli::parse(); let filter_config = FilterConfig { - include: cli.include, + include: cli.include.clone(), exclude: cli.exclude, skip_until: cli.skip_until, abi_only: cli.abi_only, @@ -135,9 +134,7 @@ async fn main() -> Result<()> { verbose: cli.verbose, release: cli.release, build_target, - experimental: sway_core::ExperimentalFlags { - new_encoding: !cli.no_encoding_v1, - }, + experimental: cli.experimental.clone(), update_output_files: cli.update_output_files, print_ir: cli .print_ir @@ -164,19 +161,17 @@ async fn main() -> Result<()> { // Run IR tests if !filter_config.first_only { println!("\n"); - ir_generation::run( - filter_config.include.as_ref(), - cli.verbose, - sway_ir::ExperimentalFlags { - new_encoding: run_config.experimental.new_encoding, - }, - ) - .instrument(tracing::trace_span!("IR")) - .await?; + ir_generation::run(filter_config.include.as_ref(), cli.verbose, &run_config) + .instrument(tracing::trace_span!("IR")) + .await?; } // Run snapshot tests - let args = vec!["t", "--release", "-p", "test"]; + let mut args = vec!["t", "--release", "-p", "test", "--"]; + if let Some(include) = cli.include.as_ref().map(|x| x.as_str()) { + args.push(include); + } + let mut t = std::process::Command::new("cargo") .args(args) .spawn() diff --git a/test/tests/tests.rs b/test/tests/tests.rs index 626864f2e74..b62be61a28b 100644 --- a/test/tests/tests.rs +++ b/test/tests/tests.rs @@ -4,6 +4,7 @@ use regex::Regex; use std::{path::PathBuf, str::FromStr, sync::Once}; static FORC_COMPILATION: Once = Once::new(); +static FORC_DOC_COMPILATION: Once = Once::new(); fn compile_forc() { let args = vec!["b", "--release", "-p", "forc"]; @@ -14,6 +15,15 @@ fn compile_forc() { assert!(o.status.success()); } +fn compile_forc_doc() { + let args = vec!["b", "--release", "-p", "forc-doc"]; + let o = std::process::Command::new("cargo") + .args(args) + .output() + .unwrap(); + assert!(o.status.success()); +} + pub fn main() { let repo_root: PathBuf = PathBuf::from_str(&std::env::var("CARGO_MANIFEST_DIR").unwrap()).unwrap(); @@ -34,10 +44,6 @@ pub fn main() { let repo_root = repo_root.clone(); Trial::test(name, move || { - FORC_COMPILATION.call_once(|| { - compile_forc(); - }); - let snapshot_toml = std::fs::read_to_string(format!("{}/snapshot.toml", dir.display()))?; let snapshot_toml = toml::from_str::(&snapshot_toml)?; @@ -61,7 +67,16 @@ pub fn main() { let _ = writeln!(&mut snapshot, "> {}", cmd); - let cmd = if let Some(cmd) = cmd.strip_prefix("forc ") { + // known commands + let cmd = if let Some(cmd) = cmd.strip_prefix("forc doc ") { + FORC_DOC_COMPILATION.call_once(|| { + compile_forc_doc(); + }); + format!("target/release/forc-doc {cmd} 1>&2") + } else if let Some(cmd) = cmd.strip_prefix("forc ") { + FORC_COMPILATION.call_once(|| { + compile_forc(); + }); format!("target/release/forc {cmd} 1>&2") } else { panic!("Not supported. Possible commands: forc") @@ -71,6 +86,7 @@ pub fn main() { .dir(repo_root.clone()) .stderr_to_stdout() .stdout_capture() + .env("COLUMNS", "10") .unchecked() .start() .unwrap();