diff --git a/docs/book/spell-check-custom-words.txt b/docs/book/spell-check-custom-words.txt index fc0eed0279..b43deaeead 100644 --- a/docs/book/spell-check-custom-words.txt +++ b/docs/book/spell-check-custom-words.txt @@ -244,4 +244,6 @@ StringArray StringSlice calldata Cfg -evm \ No newline at end of file +evm +AbiEncode +AbiDecode diff --git a/docs/book/src/debugging/index.md b/docs/book/src/debugging/index.md index 14dc3347a9..6f91ec2860 100644 --- a/docs/book/src/debugging/index.md +++ b/docs/book/src/debugging/index.md @@ -11,3 +11,48 @@ node to exercise your Sway code. Instruction-by-instruction debugging is availab - [Debugging with CLI](./debugging_with_cli.md) - [Debugging with IDE](./debugging_with_ide.md) + +## `__dbg` intrinsic function + +Sway also offers the `__dbg` intrinsic function to help debug all applications types: scripts, contracts and predicates. +When called, this intrinsic function will print the current file, line and column, together with a customizable print of the specified value. + +```sway +script; +fn main() -> u64 { + __dbg(1u64) +} +``` + +The application above will print: + +```terminal +[src/main.sw:3:5] = 1 +``` + +Structs can be customized by implementing the `Debug` trait. + +```sway +script; +struct S { } +impl Debug for S { + fn fmt(self, ref mut f: Formatter) { + f.debug_struct("S2") + .field("field1", 1) + .field("field2", "Hello") + .finish(); + } +} +fn main() -> u64 { + let _ = __dbg(S {}); + __dbg(1u64) +} +``` + +This code is very similar to what the Sway compiler generates by default for all declared types. +And this is what is printed: + +```terminal +[src/main.sw:12:13] = S2 { field1: 1, field2: "Hello" } +[src/main.sw:13:5] = 1 +``` diff --git a/docs/book/src/reference/compiler_intrinsics.md b/docs/book/src/reference/compiler_intrinsics.md index c3ba29ce0e..be1e834f24 100644 --- a/docs/book/src/reference/compiler_intrinsics.md +++ b/docs/book/src/reference/compiler_intrinsics.md @@ -165,12 +165,14 @@ __state_store_quad(key: b256, ptr: raw_ptr, slots: u64) -> bool --- ```sway -__log(val: T) +__log(val: T) where T: AbiEncode ``` **Description:** Logs value `val`. -**Constraints:** None. +**Constraints:** + +- `T` must implement AbiEncode --- @@ -335,10 +337,10 @@ __jmp_mem() --- ```sway -__slice(item: &[T; N], start: u64, end: u64) -> &[T] -__slice(item: &[T], start: u64, end: u64) -> &[T] -__slice(item: &mut [T; N], start: u64, end: u64) -> &mut [T] -__slice(item: &mut [T], start: u64, end: u64) -> &mut [T] +__slice(item: &[T; N], start: u64, end: u64) -> &[T] +__slice(item: &[T], start: u64, end: u64) -> &[T] +__slice(item: &mut [T; N], start: u64, end: u64) -> &mut [T] +__slice(item: &mut [T], start: u64, end: u64) -> &mut [T] ``` **Description:** Slices an array or another slice. @@ -358,10 +360,10 @@ Runtime bound checks are not generated, and must be done manually when and where --- ```sway -__elem_at(item: &[T; N], index: u64) -> &T -__elem_at(item: &[T], index: u64) -> &T -__elem_at(item: &mut [T; N], index: u64) -> &mut T -__elem_at(item: &mut [T], index: u64) -> &mut T +__elem_at(item: &[T; N], index: u64) -> &T +__elem_at(item: &[T], index: u64) -> &T +__elem_at(item: &mut [T; N], index: u64) -> &mut T +__elem_at(item: &mut [T], index: u64) -> &mut T ``` **Description:** Returns a reference to the indexed element. The mutability of reference is defined by the first parameter mutability. @@ -372,3 +374,37 @@ Runtime bound checks are not generated, and must be done manually when and where - `item` is a reference to an array or a reference to a slice; - when `index` is a literal, it must be smaller than `item` length; + +--- + +```sway +__dbg(value: T) -> T where T: Debug +``` + +**Description:** Automatically calls the `Debug` trait on the passed `value`, with file, line and column information. The passed value is returned without any modification, allowing `__dbg(...)` to be used inside of any expression. + +The code generated by this intrinsic function varies with the compilation mode. For example: + +```terminal +forc build <- will print everything as expected +forc build --release <- nothing will be printed +``` + +To enable code generation even on `Release` builds, the flag `force-dbg-in-release` needs to be enabled inside `forc.toml`. +Example: + +```toml +[project] +authors = ["Fuel Labs "] +license = "Apache-2.0" +entry = "main.sw" +name = "some-project" +force-dbg-in-release = true +``` + +It is strongly suggested to always remove this flag before publishing binaries as it will not have any effect when running +on real nodes and it only increases gas usage. + +**Constraints:** + +- `T` must implement Debug diff --git a/docs/book/src/sway-program-types/predicates.md b/docs/book/src/sway-program-types/predicates.md index 87cccb5949..8949dc8d6a 100644 --- a/docs/book/src/sway-program-types/predicates.md +++ b/docs/book/src/sway-program-types/predicates.md @@ -32,9 +32,3 @@ An analogy for predicates is rather than a traditional 12 or 24 word seed phrase Predicates may introspect the transaction spending their coins (inputs, outputs, script bytecode, etc.) and may take runtime arguments, either or both of which may affect the evaluation of the predicate. It is important to note that predicates cannot read or write memory. They may however check the inputs and outputs of a transaction. For example in the [OTC Predicate Swap Example](https://github.com/FuelLabs/sway-applications/tree/master/OTC-swap-predicate), a user may specify they would like to swap `asset1` for `asset2` and with amount of `5`. The user would then send `asset1` to the predicate. Only when the predicate can verify that the outputs include `5` coins of `asset2` being sent to the original user, may `asset1` be transferred out of the predicate. - -## Debugging Predicates - -Because they don't have any side effects (they are _pure_), predicates cannot create receipts. Therefore, they cannot have logging or create a stack backtrace. This means that there is no native way to debug them aside from using a single-stepping debugger. - -As a workaround, the predicate can be written, tested, and debugged first as a `script`, and then changed back into a `predicate`. diff --git a/forc-pkg/src/manifest/build_profile.rs b/forc-pkg/src/manifest/build_profile.rs index a196cd5106..0d66ade4bf 100644 --- a/forc-pkg/src/manifest/build_profile.rs +++ b/forc-pkg/src/manifest/build_profile.rs @@ -83,6 +83,10 @@ impl BuildProfile { optimization_level: OptLevel::Opt1, } } + + pub fn is_release(&self) -> bool { + self.name == Self::RELEASE + } } impl Default for BuildProfile { diff --git a/forc-pkg/src/manifest/mod.rs b/forc-pkg/src/manifest/mod.rs index 764c6017ac..83b6dbb7d0 100644 --- a/forc-pkg/src/manifest/mod.rs +++ b/forc-pkg/src/manifest/mod.rs @@ -213,6 +213,7 @@ pub struct Project { #[serde(default)] pub experimental: HashMap, pub metadata: Option, + pub force_dbg_in_release: Option, } // Validation function for the `name` field @@ -1449,6 +1450,7 @@ mod tests { forc_version: None, experimental: HashMap::new(), metadata: Some(toml::Value::from(toml::value::Table::new())), + force_dbg_in_release: None, }; let serialized = toml::to_string(&project).unwrap(); @@ -1477,6 +1479,7 @@ mod tests { forc_version: None, experimental: HashMap::new(), metadata: None, + force_dbg_in_release: None, }; let serialized = toml::to_string(&project).unwrap(); @@ -1529,7 +1532,7 @@ mod tests { name = "test-project" license = "Apache-2.0" entry = "main.sw" - + [metadata description = "Invalid TOML" "#; @@ -1542,7 +1545,7 @@ mod tests { name = "test-project" license = "Apache-2.0" entry = "main.sw" - + [metadata] ] = "Invalid key" "#; @@ -1555,7 +1558,7 @@ mod tests { name = "test-project" license = "Apache-2.0" entry = "main.sw" - + [metadata] nested = { key = "value1" } @@ -1578,7 +1581,7 @@ mod tests { name = "test-project" license = "Apache-2.0" entry = "main.sw" - + [metadata] boolean = true integer = 42 @@ -1614,13 +1617,13 @@ mod tests { let toml_str = r#" [workspace] members = ["package1", "package2"] - + [workspace.metadata] description = "A test workspace" version = "1.0.0" authors = ["Test Author"] homepage = "https://example.com" - + [workspace.metadata.ci] workflow = "main" timeout = 3600 @@ -1659,7 +1662,7 @@ mod tests { let toml_str = r#" [workspace] members = ["package1", "package2"] - + [workspace.metadata] "#; @@ -1674,15 +1677,15 @@ mod tests { let toml_str = r#" [workspace] members = ["package1", "package2"] - + [workspace.metadata] numbers = [1, 2, 3] strings = ["a", "b", "c"] mixed = [1, "two", true] - + [workspace.metadata.nested] key = "value" - + [workspace.metadata.nested.deep] another = "value" "#; diff --git a/forc-pkg/src/pkg.rs b/forc-pkg/src/pkg.rs index 2ec4596269..95dbf745f8 100644 --- a/forc-pkg/src/pkg.rs +++ b/forc-pkg/src/pkg.rs @@ -47,7 +47,7 @@ use sway_core::{ source_map::SourceMap, write_dwarf, BuildTarget, Engines, FinalizedEntry, LspConfig, }; -use sway_core::{set_bytecode_configurables_offset, PrintAsm, PrintIr}; +use sway_core::{set_bytecode_configurables_offset, DbgGeneration, PrintAsm, PrintIr}; use sway_error::{error::CompileError, handler::Handler, warning::CompileWarning}; use sway_features::ExperimentalFeatures; use sway_types::{Ident, ProgramId, Span, Spanned}; @@ -1557,6 +1557,7 @@ pub fn sway_build_config( entry_path: &Path, build_target: BuildTarget, build_profile: &BuildProfile, + dbg_generation: sway_core::DbgGeneration, ) -> Result { // Prepare the build config to pass through to the compiler. let file_name = find_file_name(manifest_dir, entry_path)?; @@ -1564,6 +1565,7 @@ pub fn sway_build_config( file_name.to_path_buf(), manifest_dir.to_path_buf(), build_target, + dbg_generation, ) .with_print_dca_graph(build_profile.print_dca_graph.clone()) .with_print_dca_graph_url_format(build_profile.print_dca_graph_url_format.clone()) @@ -1603,6 +1605,7 @@ pub fn dependency_namespace( contract_id_value: Option, program_id: ProgramId, experimental: ExperimentalFeatures, + dbg_generation: sway_core::DbgGeneration, ) -> Result> { // TODO: Clean this up when config-time constants v1 are removed. let node_idx = &graph[node]; @@ -1614,6 +1617,7 @@ pub fn dependency_namespace( program_id, contract_id_value, experimental, + dbg_generation, )? } else { Package::new(name.clone(), None, program_id, false) @@ -1646,6 +1650,7 @@ pub fn dependency_namespace( program_id, contract_id_value, experimental, + dbg_generation, )? } }; @@ -1680,12 +1685,18 @@ pub fn compile( namespace: namespace::Package, source_map: &mut SourceMap, experimental: ExperimentalFeatures, + dbg_generation: DbgGeneration, ) -> Result { let mut metrics = PerformanceData::default(); let entry_path = pkg.manifest_file.entry_path(); - let sway_build_config = - sway_build_config(pkg.manifest_file.dir(), &entry_path, pkg.target, profile)?; + let sway_build_config = sway_build_config( + pkg.manifest_file.dir(), + &entry_path, + pkg.target, + profile, + dbg_generation, + )?; let terse_mode = profile.terse; let reverse_results = profile.reverse_results; let fail = |handler: Handler| { @@ -2374,6 +2385,10 @@ pub fn build( let pkg = &plan.graph()[node]; let manifest = &plan.manifest_map()[&pkg.id()]; let program_ty = manifest.program_type().ok(); + let dbg_generation = match (profile.is_release(), manifest.project.force_dbg_in_release) { + (true, Some(true)) | (false, _) => DbgGeneration::Full, + (true, _) => DbgGeneration::None, + }; print_compiling( program_ty.as_ref(), @@ -2439,6 +2454,7 @@ pub fn build( None, program_id, experimental, + dbg_generation, ) { Ok(o) => o, Err(errs) => return fail(&[], &errs), @@ -2451,6 +2467,7 @@ pub fn build( dep_namespace, &mut source_map, experimental, + dbg_generation, )?; if let Some(outfile) = profile.metrics_outfile { @@ -2509,6 +2526,7 @@ pub fn build( contract_id_value.clone(), program_id, experimental, + dbg_generation, ) { Ok(o) => o, Err(errs) => { @@ -2530,6 +2548,7 @@ pub fn build( dep_namespace, &mut source_map, experimental, + dbg_generation, )?; if let Some(outfile) = profile.metrics_outfile { @@ -2577,6 +2596,7 @@ pub fn check( retrigger_compilation: Option>, experimental: &[sway_features::Feature], no_experimental: &[sway_features::Feature], + dbg_generation: sway_core::DbgGeneration, ) -> anyhow::Result, Handler)>> { let mut lib_namespace_map = HashMap::default(); let mut source_map = SourceMap::new(); @@ -2625,6 +2645,7 @@ pub fn check( contract_id_value, program_id, experimental, + dbg_generation, ) .expect("failed to create dependency namespace"); @@ -2638,6 +2659,7 @@ pub fn check( &manifest.entry_path(), build_target, &profile, + dbg_generation, )? .with_include_tests(include_tests) .with_lsp_mode(lsp_mode.clone()); diff --git a/forc-plugins/forc-doc/src/lib.rs b/forc-plugins/forc-doc/src/lib.rs index af601fa4aa..2dadb97ff6 100644 --- a/forc-plugins/forc-doc/src/lib.rs +++ b/forc-plugins/forc-doc/src/lib.rs @@ -110,6 +110,7 @@ pub fn compile_html( None, &build_instructions.experimental.experimental, &build_instructions.experimental.no_experimental, + sway_core::DbgGeneration::Full, )?; let raw_docs = if build_instructions.no_deps { diff --git a/forc-plugins/forc-migrate/src/cli/shared.rs b/forc-plugins/forc-migrate/src/cli/shared.rs index e92a7f0f21..1f42289317 100644 --- a/forc-plugins/forc-migrate/src/cli/shared.rs +++ b/forc-plugins/forc-migrate/src/cli/shared.rs @@ -108,6 +108,7 @@ pub(crate) fn compile_package<'a>( None, &build_instructions.experimental.experimental, &build_instructions.experimental.no_experimental, + sway_core::DbgGeneration::Full, )?; let Some(programs) = diff --git a/forc-test/src/ecal.rs b/forc-test/src/ecal.rs new file mode 100644 index 0000000000..a5ee57544d --- /dev/null +++ b/forc-test/src/ecal.rs @@ -0,0 +1,161 @@ +use fuel_vm::{ + interpreter::EcalHandler, + prelude::{Interpreter, RegId}, +}; + +// ssize_t write(int fd, const void buf[.count], size_t count); +pub const WRITE_SYSCALL: u64 = 1000; + +#[derive(Debug, Clone)] +pub enum Syscall { + Write { fd: u64, bytes: Vec }, + Unknown { ra: u64, rb: u64, rc: u64, rd: u64 }, +} + +impl Syscall { + pub fn apply(&self) { + match self { + Syscall::Write { fd, bytes } => { + let s = std::str::from_utf8(bytes.as_slice()).unwrap(); + + use std::io::Write; + use std::os::fd::FromRawFd; + + let mut f = unsafe { std::fs::File::from_raw_fd(*fd as i32) }; + write!(&mut f, "{}", s).unwrap(); + + // Dont close the fd + std::mem::forget(f); + } + Syscall::Unknown { ra, rb, rc, rd } => { + println!("Unknown ecal: {} {} {} {}", ra, rb, rc, rd); + } + } + } +} + +/// Handle VM `ecal` as syscalls. +/// +/// The application of the syscalls can be turned off, +/// guaranteeing total isolation from the outside world. +/// +/// Capture of the syscalls can be turned on, allowing +/// its application even after the VM is not running anymore. +/// +/// Supported syscalls: +/// 1000 - write(fd: u64, buf: raw_ptr, count: u64) -> u64 +#[derive(Debug, Clone)] +pub struct EcalSyscallHandler { + pub apply: bool, + pub capture: bool, + pub captured: Vec, +} + +impl Default for EcalSyscallHandler { + fn default() -> Self { + Self::only_capturing() + } +} + +impl EcalSyscallHandler { + pub fn only_capturing() -> Self { + Self { + apply: false, + capture: true, + captured: vec![], + } + } + + pub fn only_applying() -> Self { + Self { + apply: true, + capture: false, + captured: vec![], + } + } + + pub fn clear(&mut self) { + self.captured.clear(); + } +} + +impl EcalHandler for EcalSyscallHandler { + fn ecal( + vm: &mut Interpreter, + a: RegId, + b: RegId, + c: RegId, + d: RegId, + ) -> fuel_vm::error::SimpleResult<()> + where + M: fuel_vm::prelude::Memory, + { + let regs = vm.registers(); + let syscall = match regs[a.to_u8() as usize] { + WRITE_SYSCALL => { + let fd = regs[b.to_u8() as usize]; + let addr = regs[c.to_u8() as usize]; + let count = regs[d.to_u8() as usize]; + let bytes = vm.memory().read(addr, count).unwrap().to_vec(); + Syscall::Write { fd, bytes } + } + _ => { + let ra = regs[a.to_u8() as usize]; + let rb = regs[b.to_u8() as usize]; + let rc = regs[c.to_u8() as usize]; + let rd = regs[d.to_u8() as usize]; + Syscall::Unknown { ra, rb, rc, rd } + } + }; + + let s = vm.ecal_state_mut(); + + if s.apply { + syscall.apply(); + } + + if s.capture { + s.captured.push(syscall); + } + + Ok(()) + } +} + +#[test] +fn ok_capture_ecals() { + use fuel_vm::fuel_asm::op::*; + use fuel_vm::prelude::*; + let vm: Interpreter = <_>::default(); + + let test_input = "Hello, WriteSyscall!"; + let script_data: Vec = test_input.bytes().collect(); + let script = vec![ + movi(0x20, WRITE_SYSCALL as u32), + gtf_args(0x10, 0x00, GTFArgs::ScriptData), + movi(0x21, script_data.len().try_into().unwrap()), + ecal(0x20, 0x1, 0x10, 0x21), + ret(RegId::ONE), + ] + .into_iter() + .collect(); + + // Execute transaction + let mut client = MemoryClient::from_txtor(vm.into()); + let tx = TransactionBuilder::script(script, script_data) + .script_gas_limit(1_000_000) + .add_fee_input() + .finalize() + .into_checked(Default::default(), &ConsensusParameters::standard()) + .expect("failed to generate a checked tx"); + let _ = client.transact(tx); + + // Verify + let t: Transactor = client.into(); + let syscalls = t.interpreter().ecal_state().captured.clone(); + + assert_eq!(syscalls.len(), 1); + assert!( + matches!(&syscalls[0], Syscall::Write { fd: 1, bytes } if std::str::from_utf8(bytes).unwrap() == test_input) + ); +} diff --git a/forc-test/src/execute.rs b/forc-test/src/execute.rs index 33202be449..8925f7f7ab 100644 --- a/forc-test/src/execute.rs +++ b/forc-test/src/execute.rs @@ -1,3 +1,4 @@ +use crate::ecal::EcalSyscallHandler; use crate::maxed_consensus_params; use crate::setup::TestSetup; use crate::TestResult; @@ -9,11 +10,8 @@ use fuel_vm::fuel_asm; use fuel_vm::prelude::Instruction; use fuel_vm::prelude::RegId; use fuel_vm::{ - self as vm, - checked_transaction::builder::TransactionBuilderExt, - interpreter::{Interpreter, NotSupportedEcal}, - prelude::SecretKey, - storage::MemoryStorage, + self as vm, checked_transaction::builder::TransactionBuilderExt, interpreter::Interpreter, + prelude::SecretKey, storage::MemoryStorage, }; use rand::{Rng, SeedableRng}; @@ -26,7 +24,7 @@ use vm::state::ProgramState; /// An interface for executing a test within a VM [Interpreter] instance. #[derive(Debug, Clone)] pub struct TestExecutor { - pub interpreter: Interpreter, + pub interpreter: Interpreter, pub tx: vm::checked_transaction::Ready, pub test_entry: PkgTestEntry, pub name: String, @@ -212,6 +210,7 @@ impl TestExecutor { condition, logs, gas_used, + ecal: Box::new(self.interpreter.ecal_state().clone()), })) } @@ -243,10 +242,13 @@ impl TestExecutor { condition, logs, gas_used, + ecal: Box::new(self.interpreter.ecal_state().clone()), })) } pub fn execute(&mut self) -> anyhow::Result { + self.interpreter.ecal_state_mut().clear(); + let start = std::time::Instant::now(); let mut state = Ok(self.single_step_until_test()); @@ -282,6 +284,7 @@ impl TestExecutor { condition, logs, gas_used, + ecal: Box::new(self.interpreter.ecal_state().clone()), }) } diff --git a/forc-test/src/lib.rs b/forc-test/src/lib.rs index 8b2b77d96f..19552e63b8 100644 --- a/forc-test/src/lib.rs +++ b/forc-test/src/lib.rs @@ -1,3 +1,4 @@ +pub mod ecal; pub mod execute; pub mod setup; @@ -5,6 +6,7 @@ use crate::execute::TestExecutor; use crate::setup::{ ContractDeploymentSetup, ContractTestSetup, DeploymentSetup, ScriptTestSetup, TestSetup, }; +use ecal::EcalSyscallHandler; use forc_pkg::{self as pkg, BuildOpts}; use fuel_abi_types::error_codes::ErrorSignal; use fuel_tx as tx; @@ -74,6 +76,8 @@ pub struct TestResult { pub logs: Vec, /// Gas used while executing this test. pub gas_used: u64, + /// EcalState of the execution + pub ecal: Box, } const TEST_METADATA_SEED: u64 = 0x7E57u64; diff --git a/forc/src/cli/commands/test.rs b/forc/src/cli/commands/test.rs index e9abb49dbc..7fef0164eb 100644 --- a/forc/src/cli/commands/test.rs +++ b/forc/src/cli/commands/test.rs @@ -165,6 +165,10 @@ fn print_tested_pkg(pkg: &TestedPackage, test_print_opts: &TestPrintOpts) -> For info!("Decoded log value: {}, log rb: {}", var_value, rb); } } + + for captured in test.ecal.captured.iter() { + captured.apply(); + } } if test_print_opts.raw_logs { diff --git a/forc/src/ops/forc_check.rs b/forc/src/ops/forc_check.rs index 72abdd96c9..fb28cbeca1 100644 --- a/forc/src/ops/forc_check.rs +++ b/forc/src/ops/forc_check.rs @@ -50,6 +50,7 @@ pub fn check( None, &experimental.experimental, &experimental.no_experimental, + sway_core::DbgGeneration::None, )?; let (res, handler) = v .pop() diff --git a/sway-ast/src/intrinsics.rs b/sway-ast/src/intrinsics.rs index d14c4e1b85..a11346e58d 100644 --- a/sway-ast/src/intrinsics.rs +++ b/sway-ast/src/intrinsics.rs @@ -45,6 +45,7 @@ pub enum Intrinsic { Slice, // let ref_to_slice = __slice::(item: T, inclusive_start_index, exclusive_end_index) ElemAt, // let elem: &T = __elem_at::(item: T, index) Transmute, // let dst: B = __transmute::(src) + Dbg, // __dbg(value) } impl fmt::Display for Intrinsic { @@ -92,6 +93,7 @@ impl fmt::Display for Intrinsic { Intrinsic::Slice => "slice", Intrinsic::ElemAt => "elem_at", Intrinsic::Transmute => "transmute", + Intrinsic::Dbg => "dbg", }; write!(f, "{s}") } @@ -143,6 +145,7 @@ impl Intrinsic { "__slice" => Slice, "__elem_at" => ElemAt, "__transmute" => Transmute, + "__dbg" => Dbg, _ => return None, }) } diff --git a/sway-core/src/build_config.rs b/sway-core/src/build_config.rs index a87591e776..15fda8cd16 100644 --- a/sway-core/src/build_config.rs +++ b/sway-core/src/build_config.rs @@ -38,6 +38,13 @@ impl BuildTarget { pub const CFG: &'static [&'static str] = &["evm", "fuel"]; } +#[derive(Default, Clone, Copy)] +pub enum DbgGeneration { + Full, + #[default] + None, +} + #[derive(Serialize, Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default)] pub enum OptLevel { #[default] @@ -180,6 +187,7 @@ impl From<&PrintIr> for PrintPassesOpts { pub struct BuildConfig { // Build target for code generation. pub(crate) build_target: BuildTarget, + pub(crate) dbg_generation: DbgGeneration, // The canonical file path to the root module. // E.g. `/home/user/project/src/main.sw`. pub(crate) canonical_root_module: Arc, @@ -210,6 +218,7 @@ impl BuildConfig { root_module: PathBuf, canonical_manifest_dir: PathBuf, build_target: BuildTarget, + dbg_generation: DbgGeneration, ) -> Self { assert!( canonical_manifest_dir.has_root(), @@ -230,6 +239,7 @@ impl BuildConfig { }; Self { build_target, + dbg_generation, canonical_root_module: Arc::new(canonical_root_module), print_dca_graph: None, print_dca_graph_url_format: None, @@ -348,6 +358,7 @@ mod test { root_module, canonical_manifest_dir, BuildTarget::default(), + DbgGeneration::Full, ); } @@ -359,6 +370,7 @@ mod test { root_module, canonical_manifest_dir, BuildTarget::default(), + DbgGeneration::Full, ); } } diff --git a/sway-core/src/engine_threading.rs b/sway-core/src/engine_threading.rs index 2c8b3c2a41..46ffb1b1b3 100644 --- a/sway-core/src/engine_threading.rs +++ b/sway-core/src/engine_threading.rs @@ -52,11 +52,29 @@ impl Engines { /// Removes all data associated with `source_id` from the engines. /// It is intended to be used during garbage collection to remove any data that is no longer needed. + /// + /// It will also clear the associated autogenerated file for the `source_id` parameter. pub fn clear_module(&mut self, source_id: &sway_types::SourceId) { self.type_engine.clear_module(source_id); self.decl_engine.clear_module(source_id); self.parsed_decl_engine.clear_module(source_id); self.query_engine.clear_module(source_id); + + // Check if `source_id` has an associated autogenerated file + // and clear it + if let Some(autogenerated_source_id) = + self.se().get_associated_autogenerated_source_id(source_id) + { + if autogenerated_source_id == *source_id { + return; + } + + self.type_engine.clear_module(&autogenerated_source_id); + self.decl_engine.clear_module(&autogenerated_source_id); + self.parsed_decl_engine + .clear_module(&autogenerated_source_id); + self.query_engine.clear_module(&autogenerated_source_id); + } } /// Helps out some `thing: T` by adding `self` as context. diff --git a/sway-core/src/ir_generation/const_eval.rs b/sway-core/src/ir_generation/const_eval.rs index a105c9551d..9c5d0bd371 100644 --- a/sway-core/src/ir_generation/const_eval.rs +++ b/sway-core/src/ir_generation/const_eval.rs @@ -1677,6 +1677,9 @@ fn const_eval_intrinsic( let c = transmute_bytes(lookup.context, &mut cursor, &dst_ir_type)?; Ok(Some(Constant::unique(lookup.context, c))) } + Intrinsic::Dbg => { + unreachable!("__dbg should not exist in the typed tree") + } } } diff --git a/sway-core/src/ir_generation/function.rs b/sway-core/src/ir_generation/function.rs index bd5a0c2f0d..f607b76c3c 100644 --- a/sway-core/src/ir_generation/function.rs +++ b/sway-core/src/ir_generation/function.rs @@ -2210,6 +2210,9 @@ impl<'eng> FnCompiler<'eng> { Intrinsic::Transmute => { self.compile_intrinsic_transmute(arguments, return_type, context, md_mgr, &span) } + Intrinsic::Dbg => { + unreachable!("__dbg should not exist in the typed tree") + } } } diff --git a/sway-core/src/lib.rs b/sway-core/src/lib.rs index 3c469c09d4..0ee22ac272 100644 --- a/sway-core/src/lib.rs +++ b/sway-core/src/lib.rs @@ -31,6 +31,7 @@ use crate::source_map::SourceMap; pub use asm_generation::from_ir::compile_ir_context_to_finalized_asm; use asm_generation::FinalizedAsm; pub use asm_generation::{CompiledBytecode, FinalizedEntry}; +pub use build_config::DbgGeneration; pub use build_config::{BuildConfig, BuildTarget, LspConfig, OptLevel, PrintAsm, PrintIr}; use control_flow_analysis::ControlFlowGraph; pub use debug_generation::write_dwarf; @@ -98,9 +99,9 @@ pub fn parse( experimental: ExperimentalFeatures, ) -> Result<(lexed::LexedProgram, parsed::ParseProgram), ErrorEmitted> { match config { - None => parse_in_memory(handler, engines, src, experimental), + None => parse_in_memory(handler, engines, src, experimental, DbgGeneration::None), // When a `BuildConfig` is given, - // the module source may declare `dep`s that must be parsed from other files. + // the module source may declare `mod`s that must be parsed from other files. Some(config) => parse_module_tree( handler, engines, @@ -108,6 +109,7 @@ pub fn parse( config.canonical_root_module(), None, config.build_target, + config.dbg_generation, config.include_tests, experimental, config.lsp_mode.as_ref(), @@ -362,6 +364,7 @@ fn parse_in_memory( engines: &Engines, src: Source, experimental: ExperimentalFeatures, + dbg_generation: DbgGeneration, ) -> Result<(lexed::LexedProgram, parsed::ParseProgram), ErrorEmitted> { let mut hasher = DefaultHasher::new(); src.text.hash(&mut hasher); @@ -376,7 +379,7 @@ fn parse_in_memory( let attributes_error_emitted = handler.append(attributes_handler); let (kind, tree) = to_parsed_lang::convert_parse_tree( - &mut to_parsed_lang::Context::new(BuildTarget::EVM, experimental), + &mut to_parsed_lang::Context::new(BuildTarget::EVM, dbg_generation, experimental), handler, engines, module.value.clone(), @@ -425,6 +428,7 @@ fn parse_submodules( module: &sway_ast::Module, module_dir: &Path, build_target: BuildTarget, + dbg_generation: DbgGeneration, include_tests: bool, experimental: ExperimentalFeatures, lsp_mode: Option<&LspConfig>, @@ -457,6 +461,7 @@ fn parse_submodules( submod_path.clone(), Some(submod.name.as_str()), build_target, + dbg_generation, include_tests, experimental, lsp_mode, @@ -510,6 +515,7 @@ fn parse_module_tree( path: Arc, module_name: Option<&str>, build_target: BuildTarget, + dbg_generation: DbgGeneration, include_tests: bool, experimental: ExperimentalFeatures, lsp_mode: Option<&LspConfig>, @@ -530,6 +536,7 @@ fn parse_module_tree( &module.value, module_dir, build_target, + dbg_generation, include_tests, experimental, lsp_mode, @@ -544,7 +551,7 @@ fn parse_module_tree( // Convert from the raw parsed module to the `ParseTree` ready for type-check. let (kind, tree) = to_parsed_lang::convert_parse_tree( - &mut to_parsed_lang::Context::new(build_target, experimental), + &mut to_parsed_lang::Context::new(build_target, dbg_generation, experimental), handler, engines, module.value.clone(), diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/abi_encoding.rs b/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/abi_encoding.rs index ce08ba9c24..c99d1d30ba 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/abi_encoding.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/abi_encoding.rs @@ -13,7 +13,7 @@ use sway_error::{ error::CompileError, handler::{ErrorEmitted, Handler}, }; -use sway_types::{BaseIdent, Named, ProgramId, Span, Spanned}; +use sway_types::{BaseIdent, Named, SourceId, Span, Spanned}; #[derive(Default)] pub struct AbiEncodingAutoImplInfo {} @@ -190,7 +190,7 @@ where } // Auto implements AbiEncode and AbiDecode for structs and returns their `AstNode`s. - fn auto_impl_struct( + fn auto_impl_abi_encode_and_decode_for_struct( &mut self, engines: &Engines, decl: &TyDecl, @@ -208,16 +208,18 @@ where let implementing_for_decl_id = decl.to_struct_decl(&Handler::default(), engines).unwrap(); let struct_decl = self.ctx.engines().de().get(&implementing_for_decl_id); - let program_id = struct_decl.span().source_id().map(|sid| sid.program_id()); - let abi_encode_body = self.generate_abi_encode_struct_body(engines, &struct_decl); let abi_encode_code = self.generate_abi_encode_code( struct_decl.name(), &struct_decl.type_parameters, abi_encode_body, ); - let abi_encode_node = - self.parse_impl_trait_to_ty_ast_node(engines, program_id, &abi_encode_code); + let abi_encode_node = self.parse_impl_trait_to_ty_ast_node( + engines, + struct_decl.span().source_id(), + &abi_encode_code, + crate::build_config::DbgGeneration::None, + ); let abi_decode_body = self.generate_abi_decode_struct_body(engines, &struct_decl); let abi_decode_code = self.generate_abi_decode_code( @@ -225,13 +227,17 @@ where &struct_decl.type_parameters, abi_decode_body?, ); - let abi_decode_node = - self.parse_impl_trait_to_ty_ast_node(engines, program_id, &abi_decode_code); + let abi_decode_node = self.parse_impl_trait_to_ty_ast_node( + engines, + struct_decl.span().source_id(), + &abi_decode_code, + crate::build_config::DbgGeneration::None, + ); Some((abi_encode_node.ok(), abi_decode_node.ok())) } - fn auto_impl_enum( + fn auto_impl_abi_encode_and_decode_for_enum( &mut self, engines: &Engines, decl: &TyDecl, @@ -249,16 +255,18 @@ where let enum_decl_id = decl.to_enum_id(&Handler::default(), engines).unwrap(); let enum_decl = self.ctx.engines().de().get(&enum_decl_id); - let program_id = enum_decl.span().source_id().map(|sid| sid.program_id()); - let abi_encode_body = self.generate_abi_encode_enum_body(engines, &enum_decl); let abi_encode_code = self.generate_abi_encode_code( enum_decl.name(), &enum_decl.type_parameters, abi_encode_body, ); - let abi_encode_node = - self.parse_impl_trait_to_ty_ast_node(engines, program_id, &abi_encode_code); + let abi_encode_node = self.parse_impl_trait_to_ty_ast_node( + engines, + enum_decl.span().source_id(), + &abi_encode_code, + crate::build_config::DbgGeneration::None, + ); let abi_decode_body = self.generate_abi_decode_enum_body(engines, &enum_decl); let abi_decode_code = self.generate_abi_decode_code( @@ -266,8 +274,12 @@ where &enum_decl.type_parameters, abi_decode_body?, ); - let abi_decode_node = - self.parse_impl_trait_to_ty_ast_node(engines, program_id, &abi_decode_code); + let abi_decode_node = self.parse_impl_trait_to_ty_ast_node( + engines, + enum_decl.span().source_id(), + &abi_decode_code, + crate::build_config::DbgGeneration::None, + ); Some((abi_encode_node.ok(), abi_decode_node.ok())) } @@ -278,8 +290,12 @@ where decl: &ty::TyDecl, ) -> (Option, Option) { match decl { - TyDecl::StructDecl(_) => self.auto_impl_struct(engines, decl).unwrap_or((None, None)), - TyDecl::EnumDecl(_) => self.auto_impl_enum(engines, decl).unwrap_or((None, None)), + TyDecl::StructDecl(_) => self + .auto_impl_abi_encode_and_decode_for_struct(engines, decl) + .unwrap_or((None, None)), + TyDecl::EnumDecl(_) => self + .auto_impl_abi_encode_and_decode_for_enum(engines, decl) + .unwrap_or((None, None)), _ => (None, None), } } @@ -287,7 +303,7 @@ where pub(crate) fn generate_contract_entry( &mut self, engines: &Engines, - program_id: Option, + original_source_id: Option<&SourceId>, contract_fns: &[DeclId], fallback_fn: Option>, handler: &Handler, @@ -442,9 +458,10 @@ where let entry_fn = self.parse_fn_to_ty_ast_node( engines, - program_id, + original_source_id, FunctionDeclarationKind::Entry, &code, + crate::build_config::DbgGeneration::None, ); match entry_fn { @@ -465,8 +482,6 @@ where decl: &TyFunctionDecl, handler: &Handler, ) -> Result { - let program_id = decl.span.source_id().map(|sid| sid.program_id()); - let Some(args_types) = decl .parameters .iter() @@ -503,9 +518,10 @@ where let entry_fn = self.parse_fn_to_ty_ast_node( engines, - program_id, + decl.span.source_id(), FunctionDeclarationKind::Entry, &code, + crate::build_config::DbgGeneration::None, ); match entry_fn { @@ -558,8 +574,6 @@ where decl: &TyFunctionDecl, handler: &Handler, ) -> Result { - let program_id = decl.span.source_id().map(|sid| sid.program_id()); - let Some(args_types) = decl .parameters .iter() @@ -613,9 +627,10 @@ where let entry_fn = self.parse_fn_to_ty_ast_node( engines, - program_id, + decl.span.source_id(), FunctionDeclarationKind::Entry, &code, + crate::build_config::DbgGeneration::None, ); match entry_fn { diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/debug.rs b/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/debug.rs new file mode 100644 index 0000000000..20c378c2d1 --- /dev/null +++ b/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/debug.rs @@ -0,0 +1,157 @@ +use sway_error::handler::Handler; +use sway_types::{BaseIdent, Named, Spanned}; + +use crate::{ + decl_engine::DeclEngineGet, + language::ty::{self, TyAstNode, TyDecl, TyEnumDecl, TyStructDecl}, + Engines, TypeParameter, +}; + +#[derive(Default)] +pub struct DebugAutoImplInfo {} + +pub type DebugAutoImplContext<'a, 'b> = super::AutoImplContext<'a, 'b, DebugAutoImplInfo>; + +impl<'a, 'b> DebugAutoImplContext<'a, 'b> +where + 'a: 'b, +{ + pub fn generate_debug_impl( + &mut self, + engines: &Engines, + decl: &ty::TyDecl, + ) -> Option { + match decl { + TyDecl::StructDecl(_) => self.auto_impl_debug_struct(engines, decl), + TyDecl::EnumDecl(_) => self.auto_impl_debug_enum(engines, decl), + _ => None, + } + } + + // checks if the current module is a dependency of the `debug` module. + fn is_debug_dependency(&self) -> bool { + // Dependencies of the debug library in std cannot have debug implemented for them. + self.ctx.namespace.current_package_name().as_str() == "std" + && matches!( + self.ctx.namespace.current_module().name().as_str(), + "codec" + | "raw_slice" + | "raw_ptr" + | "ops" + | "primitives" + | "registers" + | "flags" + | "debug" + ) + } + + // Auto implements Debug for structs and returns their `AstNode`s. + fn auto_impl_debug_struct(&mut self, engines: &Engines, decl: &TyDecl) -> Option { + if self.is_debug_dependency() { + return None; + } + + let implementing_for_decl_id = decl.to_struct_decl(&Handler::default(), engines).unwrap(); + let struct_decl = self.ctx.engines().de().get(&implementing_for_decl_id); + + let body = self.generate_fmt_struct_body(engines, &struct_decl); + let code = self.generate_fmt_code(struct_decl.name(), &struct_decl.type_parameters, body); + let node = self.parse_impl_trait_to_ty_ast_node( + engines, + struct_decl.span().source_id(), + &code, + crate::build_config::DbgGeneration::None, + ); + + node.ok() + } + + fn generate_fmt_code( + &self, + name: &BaseIdent, + type_parameters: &[TypeParameter], + body: String, + ) -> String { + let type_parameters_declaration = + self.generate_type_parameters_declaration_code(type_parameters); + let type_parameters_constraints = + self.generate_type_parameters_constraints_code(type_parameters, "Debug"); + + let name = name.as_str(); + + format!("#[allow(dead_code, deprecated)] impl{type_parameters_declaration} Debug for {name}{type_parameters_declaration}{type_parameters_constraints} {{ + #[allow(dead_code, deprecated)] + fn fmt(self, ref mut _f: Formatter) {{ + {body} + }} + }}") + } + + fn generate_fmt_struct_body(&self, _engines: &Engines, decl: &TyStructDecl) -> String { + let mut fields = String::new(); + + for field in decl.fields.iter() { + fields.push_str(&format!( + ".field(\"{field_name}\", self.{field_name})\n", + field_name = field.name.as_str(), + )); + } + + format!( + "_f.debug_struct(\"{}\"){fields}.finish();", + decl.name().as_str() + ) + } + + // Auto implements Debug for enums and returns their `AstNode`s. + fn auto_impl_debug_enum(&mut self, engines: &Engines, decl: &TyDecl) -> Option { + if self.is_debug_dependency() { + return None; + } + + let enum_decl_id = decl.to_enum_id(&Handler::default(), engines).unwrap(); + let enum_decl = self.ctx.engines().de().get(&enum_decl_id); + + let body = self.generate_fmt_enum_body(engines, &enum_decl); + let code = self.generate_fmt_code(enum_decl.name(), &enum_decl.type_parameters, body); + let node = self.parse_impl_trait_to_ty_ast_node( + engines, + enum_decl.span().source_id(), + &code, + crate::build_config::DbgGeneration::None, + ); + + node.ok() + } + + fn generate_fmt_enum_body(&self, engines: &Engines, decl: &TyEnumDecl) -> String { + let enum_name = decl.call_path.suffix.as_str(); + + let arms = decl + .variants + .iter() + .map(|variant| { + let variant_name = variant.name.as_str(); + if engines.te().get(variant.type_argument.type_id()).is_unit() { + format!( + "{enum_name}::{variant_name} => {{ + _f.print_str(\"{variant_name}\"); + }}, \n", + enum_name = enum_name, + variant_name = variant_name + ) + } else { + format!( + "{enum_name}::{variant_name}(value) => {{ + _f.debug_tuple(\"{enum_name}\").field(value).finish(); + }}, \n", + enum_name = enum_name, + variant_name = variant_name, + ) + } + }) + .collect::(); + + format!("match self {{ {arms} }};") + } +} diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/marker_traits.rs b/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/marker_traits.rs index 28e05c10f4..ed60c10e12 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/marker_traits.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/marker_traits.rs @@ -44,15 +44,17 @@ where return None; } - let program_id = enum_decl.span().source_id().map(|sid| sid.program_id()); - let impl_enum_code = format!( "#[allow(dead_code, deprecated)] impl {marker_trait_name} for {} {{ }}", enum_decl.name() ); - let impl_enum_node = - self.parse_impl_trait_to_ty_ast_node(engines, program_id, &impl_enum_code); + let impl_enum_node = self.parse_impl_trait_to_ty_ast_node( + engines, + enum_decl.span().source_id(), + &impl_enum_code, + crate::build_config::DbgGeneration::None, + ); impl_enum_node.ok() } diff --git a/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/mod.rs b/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/mod.rs index 99b2f78123..616c5c75ac 100644 --- a/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/mod.rs +++ b/sway-core/src/semantic_analysis/ast_node/declaration/auto_impl/mod.rs @@ -1,21 +1,23 @@ //! This module contains common infrastructure for generating and parsing auto-generated code. pub mod abi_encoding; +pub mod debug; pub mod marker_traits; use std::ops::Deref; use crate::{ + build_config::DbgGeneration, engine_threading::SpannedWithEngines, language::{ parsed::{self, AstNodeContent, Declaration, FunctionDeclarationKind}, ty::{self, TyAstNode, TyDecl}, }, semantic_analysis::TypeCheckContext, - Engines, GenericArgument, TypeInfo, TypeParameter, + BuildTarget, Engines, GenericArgument, TypeInfo, TypeParameter, }; use sway_error::handler::Handler; use sway_parse::Parse; -use sway_types::{Named, ProgramId, Spanned}; +use sway_types::{Named, SourceId, Spanned}; /// Contains all information needed to auto-implement code for a certain feature. pub struct AutoImplContext<'a, 'b, I> @@ -45,19 +47,23 @@ where /// Parses `input` into the expected [Parse] type. /// The resulted [Parse] has source id set to autogenerated source id /// within the program represented by the `program_id`. - fn parse(engines: &Engines, program_id: Option, input: &str) -> T + fn parse(engines: &Engines, original_source_id: Option<&SourceId>, src: &str) -> T where T: Parse, { // Uncomment this to see what is being generated - // println!("{}", input); + // println!("{}", src); let handler = <_>::default(); - let source_id = - program_id.map(|program_id| engines.se().get_autogenerated_source_id(program_id)); + let autogenerated_source_id = original_source_id.as_ref().and_then(|source_id| { + engines + .se() + .get_associated_autogenerated_source_id(source_id) + }); - let ts = sway_parse::lex(&handler, input.into(), 0, input.len(), source_id).unwrap(); - let mut p = sway_parse::Parser::new(&handler, &ts); + let token_stream = + sway_parse::lex(&handler, src.into(), 0, src.len(), autogenerated_source_id).unwrap(); + let mut p = sway_parse::Parser::new(&handler, &token_stream); p.check_double_underscore = false; let r = p.parse(); @@ -126,18 +132,20 @@ where pub fn parse_fn_to_ty_ast_node( &mut self, engines: &Engines, - program_id: Option, + original_source_id: Option<&SourceId>, kind: FunctionDeclarationKind, code: &str, + dbg_generation: DbgGeneration, ) -> Result { let mut ctx = crate::transform::to_parsed_lang::Context::new( crate::BuildTarget::Fuel, + dbg_generation, self.ctx.experimental, ); let handler = Handler::default(); - let item = Self::parse(engines, program_id, code); + let item = Self::parse(engines, original_source_id, code); let nodes = crate::transform::to_parsed_lang::item_to_ast_nodes( &mut ctx, &handler, @@ -157,12 +165,7 @@ where panic!( "{:?} {:?}", handler, - program_id - .and_then(|x| engines.se().get_source_ids_from_program_id(x)) - .unwrap() - .iter() - .map(|x| engines.se().get_file_name(x)) - .collect::>() + original_source_id.map(|x| engines.se().get_file_name(x)) ); } assert!(!handler.has_warnings(), "{:?}", handler); @@ -206,17 +209,19 @@ where fn parse_impl_trait_to_ty_ast_node( &mut self, engines: &Engines, - program_id: Option, + original_source_id: Option<&SourceId>, code: &str, + dbg_generation: DbgGeneration, ) -> Result { let mut ctx = crate::transform::to_parsed_lang::Context::new( - crate::BuildTarget::Fuel, + BuildTarget::Fuel, + dbg_generation, self.ctx.experimental, ); let handler = Handler::default(); - let item = Self::parse(engines, program_id, code); + let item = Self::parse(engines, original_source_id, code); let nodes = crate::transform::to_parsed_lang::item_to_ast_nodes( &mut ctx, &handler, engines, item, false, None, ) diff --git a/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs b/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs index 11f68fd122..62adfdc57f 100644 --- a/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs +++ b/sway-core/src/semantic_analysis/ast_node/expression/intrinsic_function.rs @@ -110,6 +110,9 @@ impl ty::TyIntrinsicFunctionKind { Intrinsic::Transmute => { type_check_transmute(arguments, handler, kind, type_arguments, span, ctx) } + Intrinsic::Dbg => { + unreachable!("__dbg should not exist in the typed tree") + } } } } diff --git a/sway-core/src/semantic_analysis/cei_pattern_analysis.rs b/sway-core/src/semantic_analysis/cei_pattern_analysis.rs index 6ca1b065e3..e21408094c 100644 --- a/sway-core/src/semantic_analysis/cei_pattern_analysis.rs +++ b/sway-core/src/semantic_analysis/cei_pattern_analysis.rs @@ -677,7 +677,8 @@ fn effects_of_intrinsic(intr: &sway_ast::Intrinsic) -> HashSet { | EncodeBufferAsRawSlice | Slice | ElemAt - | Transmute => HashSet::new(), + | Transmute + | Dbg => HashSet::new(), } } diff --git a/sway-core/src/semantic_analysis/module.rs b/sway-core/src/semantic_analysis/module.rs index 93fc012eaa..552bfdd9e8 100644 --- a/sway-core/src/semantic_analysis/module.rs +++ b/sway-core/src/semantic_analysis/module.rs @@ -33,7 +33,8 @@ use crate::{ use super::{ declaration::auto_impl::{ - abi_encoding::AbiEncodingAutoImplContext, marker_traits::MarkerTraitsAutoImplContext, + abi_encoding::AbiEncodingAutoImplContext, debug::DebugAutoImplContext, + marker_traits::MarkerTraitsAutoImplContext, }, symbol_collection_context::SymbolCollectionContext, }; @@ -456,7 +457,7 @@ impl ty::TyModule { let mut fn_generator = AbiEncodingAutoImplContext::new(&mut ctx); if let Ok(node) = fn_generator.generate_contract_entry( engines, - parsed.span.source_id().map(|x| x.program_id()), + parsed.span.source_id(), &contract_fns, fallback_fn, handler, @@ -553,20 +554,29 @@ impl ty::TyModule { let all_abi_encode_impls = Self::get_all_impls(ctx.by_ref(), nodes, |decl| { decl.trait_name.suffix.as_str() == "AbiEncode" }); + let all_debug_impls = Self::get_all_impls(ctx.by_ref(), nodes, |decl| { + decl.trait_name.suffix.as_str() == "Debug" + }); let mut typed_nodes = vec![]; for node in nodes { - // Check if the encoding traits are explicitly implemented. - let auto_impl_encoding_traits = match &node.content { + // Check if the encoding and debug traits are explicitly implemented. + let (auto_impl_encoding_traits, auto_impl_debug_traits) = match &node.content { AstNodeContent::Declaration(Declaration::StructDeclaration(decl_id)) => { let decl = ctx.engines().pe().get_struct(decl_id); - !all_abi_encode_impls.contains_key(&decl.name) + ( + !all_abi_encode_impls.contains_key(&decl.name), + !all_debug_impls.contains_key(&decl.name), + ) } AstNodeContent::Declaration(Declaration::EnumDeclaration(decl_id)) => { let decl = ctx.engines().pe().get_enum(decl_id); - !all_abi_encode_impls.contains_key(&decl.name) + ( + !all_abi_encode_impls.contains_key(&decl.name), + !all_debug_impls.contains_key(&decl.name), + ) } - _ => false, + _ => (false, false), }; let Ok(node) = ty::TyAstNode::type_check(handler, ctx.by_ref(), node) else { @@ -593,6 +603,19 @@ impl ty::TyModule { }; } + // Auto impl debug traits only if they are not explicitly implemented + if auto_impl_debug_traits { + match &node.content { + TyAstNodeContent::Declaration(decl @ TyDecl::StructDecl(_)) + | TyAstNodeContent::Declaration(decl @ TyDecl::EnumDecl(_)) => { + let mut ctx = DebugAutoImplContext::new(&mut ctx); + let a = ctx.generate_debug_impl(engines, decl); + generated.extend(a); + } + _ => {} + } + } + // Always auto impl marker traits. If an explicit implementation exists, that will be // reported as an error when type-checking trait impls. if ctx.experimental.error_type { diff --git a/sway-core/src/semantic_analysis/namespace/contract_helpers.rs b/sway-core/src/semantic_analysis/namespace/contract_helpers.rs index 9c5c04250d..e6a8e00c65 100644 --- a/sway-core/src/semantic_analysis/namespace/contract_helpers.rs +++ b/sway-core/src/semantic_analysis/namespace/contract_helpers.rs @@ -7,6 +7,7 @@ use sway_parse::{lex, Parser}; use sway_types::{constants::CONTRACT_ID, ProgramId, Spanned}; use crate::{ + build_config::DbgGeneration, language::{ parsed::{AstNode, AstNodeContent, Declaration, ExpressionKind}, ty::{TyAstNode, TyAstNodeContent}, @@ -25,17 +26,25 @@ pub fn package_with_contract_id( program_id: ProgramId, contract_id_value: String, experimental: crate::ExperimentalFeatures, + dbg_generation: DbgGeneration, ) -> Result> { let package = Package::new(package_name, None, program_id, true); let handler = <_>::default(); - bind_contract_id_in_root_module(&handler, engines, contract_id_value, package, experimental) - .map_err(|_| { - let (errors, warnings) = handler.consume(); - assert!(warnings.is_empty()); + bind_contract_id_in_root_module( + &handler, + engines, + contract_id_value, + package, + experimental, + dbg_generation, + ) + .map_err(|_| { + let (errors, warnings) = handler.consume(); + assert!(warnings.is_empty()); - // Invariant: `.value == None` => `!errors.is_empty()`. - vec1::Vec1::try_from_vec(errors).unwrap() - }) + // Invariant: `.value == None` => `!errors.is_empty()`. + vec1::Vec1::try_from_vec(errors).unwrap() + }) } fn bind_contract_id_in_root_module( @@ -44,6 +53,7 @@ fn bind_contract_id_in_root_module( contract_id_value: String, package: Package, experimental: crate::ExperimentalFeatures, + dbg_generation: DbgGeneration, ) -> Result { // this for loop performs a miniature compilation of each const item in the config // FIXME(Centril): Stop parsing. Construct AST directly instead! @@ -61,7 +71,7 @@ fn bind_contract_id_in_root_module( let attributes = Default::default(); // convert to const decl let const_decl_id = to_parsed_lang::item_const_to_constant_declaration( - &mut to_parsed_lang::Context::new(crate::BuildTarget::EVM, experimental), + &mut to_parsed_lang::Context::new(crate::BuildTarget::EVM, dbg_generation, experimental), handler, engines, const_item, diff --git a/sway-core/src/transform/to_parsed_lang/context.rs b/sway-core/src/transform/to_parsed_lang/context.rs index efdf030e3a..8b7b56ec2f 100644 --- a/sway-core/src/transform/to_parsed_lang/context.rs +++ b/sway-core/src/transform/to_parsed_lang/context.rs @@ -1,6 +1,7 @@ use sway_features::ExperimentalFeatures; use crate::{ + build_config::DbgGeneration, language::parsed::{Declaration, TreeType}, BuildTarget, }; @@ -27,6 +28,9 @@ pub struct Context { /// The build target. build_target: BuildTarget, + /// Indicates whether the `__dbg` intrinsic generates code or not + dbg_generation: DbgGeneration, + /// The program type. program_type: Option, @@ -36,9 +40,14 @@ pub struct Context { impl Context { /// Create a new context. - pub fn new(build_target: BuildTarget, experimental: ExperimentalFeatures) -> Self { + pub fn new( + build_target: BuildTarget, + dbg_generation: DbgGeneration, + experimental: ExperimentalFeatures, + ) -> Self { Self { build_target, + dbg_generation, experimental, module_has_configurable_block: std::default::Default::default(), destructured_struct_unique_suffix: std::default::Default::default(), @@ -90,6 +99,10 @@ impl Context { self.build_target } + pub fn is_dbg_generation_full(&self) -> bool { + matches!(self.dbg_generation, DbgGeneration::Full) + } + /// Returns the program type. pub fn program_type(&self) -> Option { self.program_type 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 cf1bf3e935..dba9159332 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 @@ -2009,8 +2009,9 @@ fn expr_func_app_to_expression_kind( let (type_arguments, type_arguments_span) = convert_ty_args(context, call_seg.generics_opt)?; - // Route intrinsic calls to different AST node. + // Transform the AST of some intrinsics match Intrinsic::try_from_str(call_seg.name.as_str()) { + // "__log(arg)" becomes "__log(encode(arg))" Some(Intrinsic::Log) if context.experimental.new_encoding && last.is_none() && !is_relative_to_root => { @@ -2044,6 +2045,214 @@ fn expr_func_app_to_expression_kind( }, )); } + // "__dbg(arg)" in debug becomes "{ + // let mut f = Formatter { }; + // f.print_str("[{current_file}:{current_line}:{current_col}] = "); + // let arg = arg; + // arg.fmt(f); + // arg + // }" + Some(Intrinsic::Dbg) + if context.is_dbg_generation_full() && last.is_none() && !is_relative_to_root => + { + if arguments.len() != 1 { + return Err(handler.emit_err(CompileError::IntrinsicIncorrectNumArgs { + name: Intrinsic::Dbg.to_string(), + expected: 1, + span, + })); + } + + let f_id: String = format!("f_{}", context.next_for_unique_suffix()); + let f_ident = BaseIdent::new_no_span(f_id.to_string()); + + let f_tid = engines.te().new_unknown(); + let f_decl_pid = engines.pe().insert(VariableDeclaration { + name: f_ident.clone(), + type_ascription: GenericArgument::Type(GenericTypeArgument { + type_id: f_tid, + initial_type_id: f_tid, + span: span.clone(), + call_path_tree: None, + }), + body: Expression { + kind: ExpressionKind::Struct(Box::new(StructExpression { + resolved_call_path_binding: None, + call_path_binding: TypeBinding { + inner: CallPath { + prefixes: vec![], + suffix: BaseIdent::new_no_span("Formatter".into()), + callpath_type: CallPathType::Ambiguous, + }, + type_arguments: TypeArgs::Regular(vec![]), + span: span.clone(), + }, + fields: vec![], + })), + span: span.clone(), + }, + is_mutable: true, + }); + + fn get_current_file_from_span(engines: &Engines, span: &Span) -> String { + let Some(source_id) = span.source_id() else { + return String::new(); + }; + let current_file = engines.se().get_path(source_id); + + // find the manifest path of the current span + let program_id = engines + .se() + .get_program_id_from_manifest_path(¤t_file) + .unwrap(); + let manifest_path = engines + .se() + .get_manifest_path_from_program_id(&program_id) + .unwrap(); + let current_file = current_file + .display() + .to_string() + .replace(&manifest_path.display().to_string(), ""); + if let Some(current_file) = current_file.strip_prefix("/") { + current_file.to_string() + } else { + current_file + } + } + + fn ast_node_to_print_str(f_ident: BaseIdent, s: &str, span: &Span) -> AstNode { + AstNode { + content: AstNodeContent::Expression(Expression { + kind: ExpressionKind::MethodApplication(Box::new( + MethodApplicationExpression { + method_name_binding: TypeBinding { + inner: MethodName::FromModule { + method_name: BaseIdent::new_no_span("print_str".into()), + }, + type_arguments: TypeArgs::Regular(vec![]), + span: span.clone(), + }, + contract_call_params: vec![], + arguments: vec![ + Expression { + kind: ExpressionKind::Variable(f_ident.clone()), + span: span.clone(), + }, + Expression { + kind: ExpressionKind::Literal(Literal::String( + Span::from_string(s.to_string()), + )), + span: span.clone(), + }, + ], + }, + )), + span: span.clone(), + }), + span: span.clone(), + } + } + + let current_file = get_current_file_from_span(engines, &span); + let start_line_col = span.start_line_col_one_index(); + + let arg_id: String = format!("arg_{}", context.next_for_unique_suffix()); + let arg_ident = BaseIdent::new_no_span(arg_id.to_string()); + + let block = CodeBlock { + contents: vec![ + // let arg = arguments[0]; + statement_let_to_ast_nodes_unfold( + context, + handler, + engines, + Pattern::AmbiguousSingleIdent(arg_ident.clone()), + None, + arguments[0].clone(), + Span::dummy(), + ) + .unwrap() + .pop() + .unwrap(), + // let mut f = Formatter { }; + AstNode { + content: AstNodeContent::Declaration(Declaration::VariableDeclaration( + f_decl_pid, + )), + span: Span::dummy(), + }, + // f.print_str("[" + + ":" + + ":" + + "] = "); + ast_node_to_print_str( + f_ident.clone(), + &format!( + "[{}:{}:{}] = ", + current_file, start_line_col.line, start_line_col.col + ), + &span, + ), + // arg.fmt(f); + AstNode { + content: AstNodeContent::Expression(Expression { + kind: ExpressionKind::MethodApplication(Box::new( + MethodApplicationExpression { + method_name_binding: TypeBinding { + inner: MethodName::FromModule { + method_name: BaseIdent::new_no_span("fmt".into()), + }, + type_arguments: TypeArgs::Regular(vec![]), + span: Span::dummy(), + }, + contract_call_params: vec![], + arguments: vec![ + Expression { + kind: ExpressionKind::Variable(arg_ident.clone()), + span: Span::dummy(), + }, + Expression { + kind: ExpressionKind::Variable(f_ident.clone()), + span: Span::dummy(), + }, + ], + }, + )), + span: Span::dummy(), + }), + span: Span::dummy(), + }, + // f.print_str(); + ast_node_to_print_str(f_ident.clone(), "\n", &span), + // arg + AstNode { + content: AstNodeContent::Expression(Expression { + kind: ExpressionKind::ImplicitReturn(Box::new(Expression { + kind: ExpressionKind::AmbiguousVariableExpression( + arg_ident.clone(), + ), + span: Span::dummy(), + })), + span: Span::dummy(), + }), + span: Span::dummy(), + }, + ], + whole_block_span: Span::dummy(), + }; + + return Ok(ExpressionKind::CodeBlock(block)); + } + // ... and in release becomes "arg" + Some(Intrinsic::Dbg) + if !context.is_dbg_generation_full() && last.is_none() && !is_relative_to_root => + { + if arguments.len() != 1 { + return Err(handler.emit_err(CompileError::IntrinsicIncorrectNumArgs { + name: Intrinsic::Dbg.to_string(), + expected: 1, + span, + })); + } + return Ok(arguments[0].kind.clone()); + } Some(intrinsic) if last.is_none() && !is_relative_to_root => { return Ok(ExpressionKind::IntrinsicFunction( IntrinsicFunctionExpression { diff --git a/sway-error/src/error.rs b/sway-error/src/error.rs index 710237ded2..be29f3a6c2 100644 --- a/sway-error/src/error.rs +++ b/sway-error/src/error.rs @@ -917,7 +917,7 @@ pub enum CompileError { span: Span, hint: String, }, - #[error("Call to \"{name}\" expects {expected} arguments")] + #[error("Call to \"{name}\" expects {expected} argument(s)")] IntrinsicIncorrectNumArgs { name: String, expected: u64, diff --git a/sway-lib-std/generate.sh b/sway-lib-std/generate.sh index 9e5f3624d4..6c1a4ef4ba 100755 --- a/sway-lib-std/generate.sh +++ b/sway-lib-std/generate.sh @@ -2,14 +2,14 @@ # Needs to exist at least one line between them remove_generated_code() { - START=`grep -n "BEGIN $1" ./src/codec.sw` + START=`grep -n "BEGIN $1" ./src/$2` START=${START%:*} - END=`grep -n "END $1" ./src/codec.sw` + END=`grep -n "END $1" ./src/$2` END=${END%:*} - sed -i "$((START+1)),$((END-1))d" ./src/codec.sw + sed -i "$((START+1)),$((END-1))d" ./src/$2 } -remove_generated_code "ARRAY_ENCODE" +remove_generated_code "ARRAY_ENCODE" "codec.sw" START=1 END=64 for ((i=END;i>=START;i--)); do @@ -17,7 +17,7 @@ for ((i=END;i>=START;i--)); do sed -i "s/\/\/ BEGIN ARRAY_ENCODE/\/\/ BEGIN ARRAY_ENCODE\n$CODE/g" ./src/codec.sw done -remove_generated_code "ARRAY_DECODE" +remove_generated_code "ARRAY_DECODE" "codec.sw" START=1 END=64 for ((i=END;i>=START;i--)); do @@ -25,7 +25,7 @@ for ((i=END;i>=START;i--)); do sed -i "s/\/\/ BEGIN ARRAY_DECODE/\/\/ BEGIN ARRAY_DECODE\n$CODE/g" ./src/codec.sw done -remove_generated_code "STRARRAY_ENCODE" +remove_generated_code "STRARRAY_ENCODE" "codec.sw" START=1 END=64 for ((i=END;i>=START;i--)); do @@ -33,7 +33,7 @@ for ((i=END;i>=START;i--)); do sed -i "s/\/\/ BEGIN STRARRAY_ENCODE/\/\/ BEGIN STRARRAY_ENCODE\n$CODE/g" ./src/codec.sw done -remove_generated_code "STRARRAY_DECODE" +remove_generated_code "STRARRAY_DECODE" "codec.sw" START=1 END=64 for ((i=END;i>=START;i--)); do @@ -78,7 +78,7 @@ generate_tuple_encode() { sed -i "s/\/\/ BEGIN TUPLES_ENCODE/\/\/ BEGIN TUPLES_ENCODE\n$CODE/g" ./src/codec.sw } -remove_generated_code "TUPLES_ENCODE" +remove_generated_code "TUPLES_ENCODE" "codec.sw" generate_tuple_encode "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z" generate_tuple_encode "A B C D E F G H I J K L M N O P Q R S T U V W X Y" generate_tuple_encode "A B C D E F G H I J K L M N O P Q R S T U V W X" @@ -141,7 +141,7 @@ generate_tuple_decode() { sed -i "s/\/\/ BEGIN TUPLES_DECODE/\/\/ BEGIN TUPLES_DECODE\n$CODE/g" ./src/codec.sw } -remove_generated_code "TUPLES_DECODE" +remove_generated_code "TUPLES_DECODE" "codec.sw" generate_tuple_decode "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z" generate_tuple_decode "A B C D E F G H I J K L M N O P Q R S T U V W X Y" generate_tuple_decode "A B C D E F G H I J K L M N O P Q R S T U V W X" @@ -168,3 +168,86 @@ generate_tuple_decode "A B C D" generate_tuple_decode "A B C" generate_tuple_decode "A B" generate_tuple_decode "A" + +generate_tuple_debug() { + local CODE="impl<" + + local elements=("$1") + for element in ${elements[@]} + do + CODE="$CODE $element," + done + + CODE="$CODE> Debug for (" + + for element in ${elements[@]} + do + CODE="$CODE $element," + done + + CODE="$CODE) where " + + for element in ${elements[@]} + do + CODE="$CODE $element: Debug, " + done + + CODE="$CODE{ fn fmt(self, ref mut f: Formatter) { let mut f = f.debug_tuple(\"\");" + + i=0 + for element in ${elements[@]} + do + CODE="$CODE let mut f = f.field(self.$i);" + i=$((i+1)) + done + + CODE="$CODE f.finish(); } }" + + sed -i "s/\/\/ BEGIN TUPLES_DEBUG/\/\/ BEGIN TUPLES_DEBUG\n$CODE/g" ./src/debug.sw +} + +remove_generated_code "TUPLES_DEBUG" "debug.sw" +generate_tuple_debug "A B C D E F G H I J K L M N O P Q R S T U V W X Y Z" +generate_tuple_debug "A B C D E F G H I J K L M N O P Q R S T U V W X Y" +generate_tuple_debug "A B C D E F G H I J K L M N O P Q R S T U V W X" +generate_tuple_debug "A B C D E F G H I J K L M N O P Q R S T U V W" +generate_tuple_debug "A B C D E F G H I J K L M N O P Q R S T U V" +generate_tuple_debug "A B C D E F G H I J K L M N O P Q R S T U" +generate_tuple_debug "A B C D E F G H I J K L M N O P Q R S T" +generate_tuple_debug "A B C D E F G H I J K L M N O P Q R S" +generate_tuple_debug "A B C D E F G H I J K L M N O P Q R" +generate_tuple_debug "A B C D E F G H I J K L M N O P Q" +generate_tuple_debug "A B C D E F G H I J K L M N O P" +generate_tuple_debug "A B C D E F G H I J K L M N O" +generate_tuple_debug "A B C D E F G H I J K L M N" +generate_tuple_debug "A B C D E F G H I J K L M" +generate_tuple_debug "A B C D E F G H I J K L" +generate_tuple_debug "A B C D E F G H I J K" +generate_tuple_debug "A B C D E F G H I J" +generate_tuple_debug "A B C D E F G H I" +generate_tuple_debug "A B C D E F G H" +generate_tuple_debug "A B C D E F G" +generate_tuple_debug "A B C D E F" +generate_tuple_debug "A B C D E" +generate_tuple_debug "A B C D" +generate_tuple_debug "A B C" +generate_tuple_debug "A B" +generate_tuple_debug "A" + +remove_generated_code "STRARRAY_DEBUG" "debug.sw" +START=1 +END=64 +for ((i=END;i>=START;i--)); do + CODE="impl Debug for str[$i] { fn fmt(self, ref mut f: Formatter) { use ::str::*; from_str_array(self).fmt(f); } }" + sed -i "s/\/\/ BEGIN STRARRAY_DEBUG/\/\/ BEGIN STRARRAY_DEBUG\n$CODE/g" ./src/debug.sw +done + +remove_generated_code "ARRAY_DEBUG" "debug.sw" +START=1 +END=64 +for ((i=END;i>=START;i--)); do + CODE="#[cfg(experimental_const_generics = false)]\nimpl Debug for [T; $i] where T: Debug { fn fmt(self, ref mut f: Formatter) { let mut f = f.debug_list(); let mut i = 0; while i < $i { f = f.entry(self[i]); i += 1; }; f.finish(); } }" + sed -i "s/\/\/ BEGIN ARRAY_DEBUG/\/\/ BEGIN ARRAY_DEBUG\n$CODE/g" ./src/debug.sw +done + +cargo r -p forc-fmt --release -- -p . \ No newline at end of file diff --git a/sway-lib-std/src/debug.sw b/sway-lib-std/src/debug.sw new file mode 100644 index 0000000000..3e6ec369e6 --- /dev/null +++ b/sway-lib-std/src/debug.sw @@ -0,0 +1,2576 @@ +library; + +use ::raw_ptr::*; +use ::codec::*; + +const STDERR: u64 = 2; + +// ssize_t write(int fd, const void buf[.count], size_t count); +fn syscall_write(fd: u64, buf: raw_ptr, count: u64) { + asm(id: 1000, fd: fd, buf: buf, count: count) { + ecal id fd buf count; + } +} + +pub struct DebugStruct { + f: Formatter, + has_fields: bool, +} + +pub struct DebugList { + f: Formatter, + has_entries: bool, +} + +pub struct DebugTuple { + f: Formatter, + has_fields: bool, +} + +pub struct Formatter {} + +impl Formatter { + pub fn print_newline(self) { + let lf = [10u8]; + syscall_write(STDERR, __addr_of(lf), 1); + } + + pub fn print_str(self, s: str) { + syscall_write(STDERR, s.as_ptr(), s.len()); + } + + pub fn print_u8(self, value: u8) { + let mut value = value; + let mut digits = [0u8; 64]; + let mut i = 63; + while value > 0 { + let digit = value % 10; + digits[i] = digit + 48; // ascii zero = 48 + i -= 1; + value = value / 10; + } + + syscall_write(STDERR, __addr_of(digits).add::(i), 64 - i); + } + + pub fn print_u16(self, value: u16) { + let mut value = value; + let mut digits = [0u8; 64]; + let mut i = 63; + while value > 0 { + let digit = asm(v: value % 10) { + v: u8 + }; + digits[i] = digit + 48; // ascii zero = 48 + i -= 1; + value = value / 10; + } + + syscall_write(STDERR, __addr_of(digits).add::(i), 64 - i); + } + + pub fn print_u32(self, value: u32) { + let mut value = value; + let mut digits = [0u8; 64]; + let mut i = 63; + while value > 0 { + let digit = asm(v: value % 10) { + v: u8 + }; + digits[i] = digit + 48; // ascii zero = 48 + i -= 1; + value = value / 10; + } + + syscall_write(STDERR, __addr_of(digits).add::(i), 64 - i); + } + + pub fn print_u64(self, value: u64) { + let mut value = value; + let mut digits = [0u8; 64]; + let mut i = 63; + while value > 0 { + let digit = asm(v: value % 10) { + v: u8 + }; + digits[i] = digit + 48; // ascii zero = 48 + i -= 1; + value = value / 10; + } + + syscall_write(STDERR, __addr_of(digits).add::(i), 64 - i); + } + + pub fn print_u256(self, value: u256) { + let mut value = value; + // u256::MAX = 115792089237316195423570985008687907853269984665640564039457584007913129639935 + let mut digits = [0u8; 80]; + let mut i = 79; + while value > 0 { + let rem = value % 10; + let (_, _, _, digit) = asm(rem: rem) { + rem: (u64, u64, u64, u64) + }; + let digit = asm(v: digit % 10) { + v: u8 + }; + digits[i] = digit + 48; // ascii zero = 48 + i -= 1; + value = value / 10; + } + + syscall_write(STDERR, __addr_of(digits).add::(i), 80 - i); + } + + pub fn debug_struct(self, name: str) -> DebugStruct { + self.print_str(name); + self.print_str(" { "); + DebugStruct { + f: self, + has_fields: false, + } + } + + pub fn debug_list(self) -> DebugList { + self.print_str("["); + DebugList { + f: self, + has_entries: false, + } + } + + pub fn debug_tuple(self, name: str) -> DebugTuple { + if name.len() > 0 { + self.print_str(name); + } + + self.print_str("("); + DebugTuple { + f: self, + has_fields: false, + } + } +} + +impl DebugStruct { + pub fn finish(ref mut self) { + if self.has_fields { + self.f.print_str(" "); + } + self.f.print_str("}"); + } + + pub fn field(ref mut self, name: str, value: T) -> Self + where + T: Debug, + { + if self.has_fields { + self.f.print_str(", "); + } + + self.f.print_str(name); + self.f.print_str(": "); + value.fmt(self.f); + + self.has_fields = true; + + self + } +} + +impl DebugList { + pub fn finish(ref mut self) { + self.f.print_str("]"); + } + + pub fn entry(ref mut self, value: T) -> Self + where + T: Debug, + { + if self.has_entries { + self.f.print_str(", "); + } + + value.fmt(self.f); + + self.has_entries = true; + + self + } +} + +impl DebugTuple { + pub fn finish(ref mut self) { + self.f.print_str(")"); + } + + pub fn field(ref mut self, value: T) -> Self + where + T: Debug, + { + if self.has_fields { + self.f.print_str(", "); + } + + value.fmt(self.f); + + self.has_fields = true; + + self + } +} + +pub trait Debug { + fn fmt(self, ref mut f: Formatter); +} + +impl Debug for u8 { + fn fmt(self, ref mut f: Formatter) { + f.print_u8(self); + } +} + +impl Debug for u16 { + fn fmt(self, ref mut f: Formatter) { + f.print_u16(self); + } +} + +impl Debug for u32 { + fn fmt(self, ref mut f: Formatter) { + f.print_u32(self); + } +} + +impl Debug for u64 { + fn fmt(self, ref mut f: Formatter) { + f.print_u64(self); + } +} + +impl Debug for u256 { + fn fmt(self, ref mut f: Formatter) { + f.print_u256(self); + } +} + +impl Debug for raw_ptr { + fn fmt(self, ref mut f: Formatter) { + let v = asm(v: self) { + v: u64 + }; + f.print_u64(v); + } +} + +impl Debug for str { + fn fmt(self, ref mut f: Formatter) { + let quote = [34u8]; + f.print_str(asm(s: (__addr_of(quote), 1)) { + s: str + }); + f.print_str(self); + f.print_str(asm(s: (__addr_of(quote), 1)) { + s: str + }); + } +} + +#[cfg(experimental_const_generics = true)] +impl Debug for [T; N] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + + let mut i = 0; + while i < N { + f = f.entry(self[i]); + i += 1; + } + + f.finish(); + } +} + +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 0] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + f.finish(); + } +} + +// BEGIN ARRAY_DEBUG +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 1] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 1 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 2] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 2 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 3] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 3 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 4] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 4 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 5] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 5 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 6] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 6 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 7] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 7 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 8] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 8 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 9] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 9 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 10] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 10 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 11] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 11 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 12] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 12 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 13] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 13 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 14] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 14 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 15] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 15 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 16] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 16 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 17] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 17 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 18] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 18 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 19] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 19 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 20] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 20 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 21] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 21 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 22] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 22 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 23] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 23 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 24] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 24 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 25] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 25 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 26] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 26 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 27] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 27 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 28] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 28 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 29] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 29 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 30] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 30 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 31] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 31 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 32] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 32 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 33] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 33 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 34] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 34 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 35] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 35 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 36] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 36 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 37] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 37 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 38] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 38 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 39] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 39 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 40] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 40 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 41] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 41 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 42] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 42 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 43] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 43 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 44] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 44 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 45] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 45 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 46] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 46 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 47] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 47 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 48] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 48 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 49] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 49 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 50] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 50 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 51] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 51 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 52] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 52 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 53] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 53 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 54] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 54 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 55] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 55 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 56] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 56 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 57] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 57 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 58] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 58 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 59] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 59 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 60] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 60 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 61] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 61 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 62] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 62 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 63] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 63 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +#[cfg(experimental_const_generics = false)] +impl Debug for [T; 64] +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_list(); + let mut i = 0; + while i < 64 { + f = f.entry(self[i]); + i += 1; + }; + f.finish(); + } +} +// END ARRAY_DEBUG + +impl Debug for str[0] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} + +// BEGIN STRARRAY_DEBUG +impl Debug for str[1] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[2] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[3] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[4] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[5] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[6] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[7] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[8] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[9] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[10] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[11] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[12] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[13] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[14] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[15] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[16] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[17] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[18] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[19] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[20] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[21] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[22] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[23] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[24] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[25] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[26] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[27] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[28] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[29] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[30] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[31] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[32] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[33] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[34] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[35] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[36] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[37] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[38] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[39] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[40] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[41] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[42] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[43] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[44] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[45] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[46] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[47] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[48] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[49] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[50] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[51] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[52] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[53] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[54] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[55] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[56] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[57] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[58] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[59] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[60] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[61] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[62] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[63] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +impl Debug for str[64] { + fn fmt(self, ref mut f: Formatter) { + use ::str::*; + from_str_array(self).fmt(f); + } +} +// END STRARRAY_DEBUG + +// BEGIN TUPLES_DEBUG +impl Debug for (A, ) +where + A: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_tuple(""); + let mut f = f.field(self.0); + f.finish(); + } +} +impl Debug for (A, B) +where + A: Debug, + B: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_tuple(""); + let mut f = f.field(self.0); + let mut f = f.field(self.1); + f.finish(); + } +} +impl Debug for (A, B, C) +where + A: Debug, + B: Debug, + C: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_tuple(""); + let mut f = f.field(self.0); + let mut f = f.field(self.1); + let mut f = f.field(self.2); + f.finish(); + } +} +impl Debug for (A, B, C, D) +where + A: Debug, + B: Debug, + C: Debug, + D: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_tuple(""); + let mut f = f.field(self.0); + let mut f = f.field(self.1); + let mut f = f.field(self.2); + let mut f = f.field(self.3); + f.finish(); + } +} +impl Debug for (A, B, C, D, E) +where + A: Debug, + B: Debug, + C: Debug, + D: Debug, + E: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_tuple(""); + let mut f = f.field(self.0); + let mut f = f.field(self.1); + let mut f = f.field(self.2); + let mut f = f.field(self.3); + let mut f = f.field(self.4); + f.finish(); + } +} +impl Debug for (A, B, C, D, E, F) +where + A: Debug, + B: Debug, + C: Debug, + D: Debug, + E: Debug, + F: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_tuple(""); + let mut f = f.field(self.0); + let mut f = f.field(self.1); + let mut f = f.field(self.2); + let mut f = f.field(self.3); + let mut f = f.field(self.4); + let mut f = f.field(self.5); + f.finish(); + } +} +impl Debug for (A, B, C, D, E, F, G) +where + A: Debug, + B: Debug, + C: Debug, + D: Debug, + E: Debug, + F: Debug, + G: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_tuple(""); + let mut f = f.field(self.0); + let mut f = f.field(self.1); + let mut f = f.field(self.2); + let mut f = f.field(self.3); + let mut f = f.field(self.4); + let mut f = f.field(self.5); + let mut f = f.field(self.6); + f.finish(); + } +} +impl Debug for (A, B, C, D, E, F, G, H) +where + A: Debug, + B: Debug, + C: Debug, + D: Debug, + E: Debug, + F: Debug, + G: Debug, + H: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_tuple(""); + let mut f = f.field(self.0); + let mut f = f.field(self.1); + let mut f = f.field(self.2); + let mut f = f.field(self.3); + let mut f = f.field(self.4); + let mut f = f.field(self.5); + let mut f = f.field(self.6); + let mut f = f.field(self.7); + f.finish(); + } +} +impl Debug for (A, B, C, D, E, F, G, H, I) +where + A: Debug, + B: Debug, + C: Debug, + D: Debug, + E: Debug, + F: Debug, + G: Debug, + H: Debug, + I: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_tuple(""); + let mut f = f.field(self.0); + let mut f = f.field(self.1); + let mut f = f.field(self.2); + let mut f = f.field(self.3); + let mut f = f.field(self.4); + let mut f = f.field(self.5); + let mut f = f.field(self.6); + let mut f = f.field(self.7); + let mut f = f.field(self.8); + f.finish(); + } +} +impl Debug for (A, B, C, D, E, F, G, H, I, J) +where + A: Debug, + B: Debug, + C: Debug, + D: Debug, + E: Debug, + F: Debug, + G: Debug, + H: Debug, + I: Debug, + J: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_tuple(""); + let mut f = f.field(self.0); + let mut f = f.field(self.1); + let mut f = f.field(self.2); + let mut f = f.field(self.3); + let mut f = f.field(self.4); + let mut f = f.field(self.5); + let mut f = f.field(self.6); + let mut f = f.field(self.7); + let mut f = f.field(self.8); + let mut f = f.field(self.9); + f.finish(); + } +} +impl Debug for (A, B, C, D, E, F, G, H, I, J, K) +where + A: Debug, + B: Debug, + C: Debug, + D: Debug, + E: Debug, + F: Debug, + G: Debug, + H: Debug, + I: Debug, + J: Debug, + K: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_tuple(""); + let mut f = f.field(self.0); + let mut f = f.field(self.1); + let mut f = f.field(self.2); + let mut f = f.field(self.3); + let mut f = f.field(self.4); + let mut f = f.field(self.5); + let mut f = f.field(self.6); + let mut f = f.field(self.7); + let mut f = f.field(self.8); + let mut f = f.field(self.9); + let mut f = f.field(self.10); + f.finish(); + } +} +impl Debug for (A, B, C, D, E, F, G, H, I, J, K, L) +where + A: Debug, + B: Debug, + C: Debug, + D: Debug, + E: Debug, + F: Debug, + G: Debug, + H: Debug, + I: Debug, + J: Debug, + K: Debug, + L: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_tuple(""); + let mut f = f.field(self.0); + let mut f = f.field(self.1); + let mut f = f.field(self.2); + let mut f = f.field(self.3); + let mut f = f.field(self.4); + let mut f = f.field(self.5); + let mut f = f.field(self.6); + let mut f = f.field(self.7); + let mut f = f.field(self.8); + let mut f = f.field(self.9); + let mut f = f.field(self.10); + let mut f = f.field(self.11); + f.finish(); + } +} +impl Debug for (A, B, C, D, E, F, G, H, I, J, K, L, M) +where + A: Debug, + B: Debug, + C: Debug, + D: Debug, + E: Debug, + F: Debug, + G: Debug, + H: Debug, + I: Debug, + J: Debug, + K: Debug, + L: Debug, + M: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_tuple(""); + let mut f = f.field(self.0); + let mut f = f.field(self.1); + let mut f = f.field(self.2); + let mut f = f.field(self.3); + let mut f = f.field(self.4); + let mut f = f.field(self.5); + let mut f = f.field(self.6); + let mut f = f.field(self.7); + let mut f = f.field(self.8); + let mut f = f.field(self.9); + let mut f = f.field(self.10); + let mut f = f.field(self.11); + let mut f = f.field(self.12); + f.finish(); + } +} +impl Debug for (A, B, C, D, E, F, G, H, I, J, K, L, M, N) +where + A: Debug, + B: Debug, + C: Debug, + D: Debug, + E: Debug, + F: Debug, + G: Debug, + H: Debug, + I: Debug, + J: Debug, + K: Debug, + L: Debug, + M: Debug, + N: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_tuple(""); + let mut f = f.field(self.0); + let mut f = f.field(self.1); + let mut f = f.field(self.2); + let mut f = f.field(self.3); + let mut f = f.field(self.4); + let mut f = f.field(self.5); + let mut f = f.field(self.6); + let mut f = f.field(self.7); + let mut f = f.field(self.8); + let mut f = f.field(self.9); + let mut f = f.field(self.10); + let mut f = f.field(self.11); + let mut f = f.field(self.12); + let mut f = f.field(self.13); + f.finish(); + } +} +impl Debug for (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O) +where + A: Debug, + B: Debug, + C: Debug, + D: Debug, + E: Debug, + F: Debug, + G: Debug, + H: Debug, + I: Debug, + J: Debug, + K: Debug, + L: Debug, + M: Debug, + N: Debug, + O: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_tuple(""); + let mut f = f.field(self.0); + let mut f = f.field(self.1); + let mut f = f.field(self.2); + let mut f = f.field(self.3); + let mut f = f.field(self.4); + let mut f = f.field(self.5); + let mut f = f.field(self.6); + let mut f = f.field(self.7); + let mut f = f.field(self.8); + let mut f = f.field(self.9); + let mut f = f.field(self.10); + let mut f = f.field(self.11); + let mut f = f.field(self.12); + let mut f = f.field(self.13); + let mut f = f.field(self.14); + f.finish(); + } +} +impl Debug for (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P) +where + A: Debug, + B: Debug, + C: Debug, + D: Debug, + E: Debug, + F: Debug, + G: Debug, + H: Debug, + I: Debug, + J: Debug, + K: Debug, + L: Debug, + M: Debug, + N: Debug, + O: Debug, + P: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_tuple(""); + let mut f = f.field(self.0); + let mut f = f.field(self.1); + let mut f = f.field(self.2); + let mut f = f.field(self.3); + let mut f = f.field(self.4); + let mut f = f.field(self.5); + let mut f = f.field(self.6); + let mut f = f.field(self.7); + let mut f = f.field(self.8); + let mut f = f.field(self.9); + let mut f = f.field(self.10); + let mut f = f.field(self.11); + let mut f = f.field(self.12); + let mut f = f.field(self.13); + let mut f = f.field(self.14); + let mut f = f.field(self.15); + f.finish(); + } +} +impl Debug for (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q) +where + A: Debug, + B: Debug, + C: Debug, + D: Debug, + E: Debug, + F: Debug, + G: Debug, + H: Debug, + I: Debug, + J: Debug, + K: Debug, + L: Debug, + M: Debug, + N: Debug, + O: Debug, + P: Debug, + Q: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_tuple(""); + let mut f = f.field(self.0); + let mut f = f.field(self.1); + let mut f = f.field(self.2); + let mut f = f.field(self.3); + let mut f = f.field(self.4); + let mut f = f.field(self.5); + let mut f = f.field(self.6); + let mut f = f.field(self.7); + let mut f = f.field(self.8); + let mut f = f.field(self.9); + let mut f = f.field(self.10); + let mut f = f.field(self.11); + let mut f = f.field(self.12); + let mut f = f.field(self.13); + let mut f = f.field(self.14); + let mut f = f.field(self.15); + let mut f = f.field(self.16); + f.finish(); + } +} +impl Debug for (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R) +where + A: Debug, + B: Debug, + C: Debug, + D: Debug, + E: Debug, + F: Debug, + G: Debug, + H: Debug, + I: Debug, + J: Debug, + K: Debug, + L: Debug, + M: Debug, + N: Debug, + O: Debug, + P: Debug, + Q: Debug, + R: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_tuple(""); + let mut f = f.field(self.0); + let mut f = f.field(self.1); + let mut f = f.field(self.2); + let mut f = f.field(self.3); + let mut f = f.field(self.4); + let mut f = f.field(self.5); + let mut f = f.field(self.6); + let mut f = f.field(self.7); + let mut f = f.field(self.8); + let mut f = f.field(self.9); + let mut f = f.field(self.10); + let mut f = f.field(self.11); + let mut f = f.field(self.12); + let mut f = f.field(self.13); + let mut f = f.field(self.14); + let mut f = f.field(self.15); + let mut f = f.field(self.16); + let mut f = f.field(self.17); + f.finish(); + } +} +impl Debug for (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S) +where + A: Debug, + B: Debug, + C: Debug, + D: Debug, + E: Debug, + F: Debug, + G: Debug, + H: Debug, + I: Debug, + J: Debug, + K: Debug, + L: Debug, + M: Debug, + N: Debug, + O: Debug, + P: Debug, + Q: Debug, + R: Debug, + S: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_tuple(""); + let mut f = f.field(self.0); + let mut f = f.field(self.1); + let mut f = f.field(self.2); + let mut f = f.field(self.3); + let mut f = f.field(self.4); + let mut f = f.field(self.5); + let mut f = f.field(self.6); + let mut f = f.field(self.7); + let mut f = f.field(self.8); + let mut f = f.field(self.9); + let mut f = f.field(self.10); + let mut f = f.field(self.11); + let mut f = f.field(self.12); + let mut f = f.field(self.13); + let mut f = f.field(self.14); + let mut f = f.field(self.15); + let mut f = f.field(self.16); + let mut f = f.field(self.17); + let mut f = f.field(self.18); + f.finish(); + } +} +impl Debug for (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T) +where + A: Debug, + B: Debug, + C: Debug, + D: Debug, + E: Debug, + F: Debug, + G: Debug, + H: Debug, + I: Debug, + J: Debug, + K: Debug, + L: Debug, + M: Debug, + N: Debug, + O: Debug, + P: Debug, + Q: Debug, + R: Debug, + S: Debug, + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_tuple(""); + let mut f = f.field(self.0); + let mut f = f.field(self.1); + let mut f = f.field(self.2); + let mut f = f.field(self.3); + let mut f = f.field(self.4); + let mut f = f.field(self.5); + let mut f = f.field(self.6); + let mut f = f.field(self.7); + let mut f = f.field(self.8); + let mut f = f.field(self.9); + let mut f = f.field(self.10); + let mut f = f.field(self.11); + let mut f = f.field(self.12); + let mut f = f.field(self.13); + let mut f = f.field(self.14); + let mut f = f.field(self.15); + let mut f = f.field(self.16); + let mut f = f.field(self.17); + let mut f = f.field(self.18); + let mut f = f.field(self.19); + f.finish(); + } +} +impl Debug for (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U) +where + A: Debug, + B: Debug, + C: Debug, + D: Debug, + E: Debug, + F: Debug, + G: Debug, + H: Debug, + I: Debug, + J: Debug, + K: Debug, + L: Debug, + M: Debug, + N: Debug, + O: Debug, + P: Debug, + Q: Debug, + R: Debug, + S: Debug, + T: Debug, + U: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_tuple(""); + let mut f = f.field(self.0); + let mut f = f.field(self.1); + let mut f = f.field(self.2); + let mut f = f.field(self.3); + let mut f = f.field(self.4); + let mut f = f.field(self.5); + let mut f = f.field(self.6); + let mut f = f.field(self.7); + let mut f = f.field(self.8); + let mut f = f.field(self.9); + let mut f = f.field(self.10); + let mut f = f.field(self.11); + let mut f = f.field(self.12); + let mut f = f.field(self.13); + let mut f = f.field(self.14); + let mut f = f.field(self.15); + let mut f = f.field(self.16); + let mut f = f.field(self.17); + let mut f = f.field(self.18); + let mut f = f.field(self.19); + let mut f = f.field(self.20); + f.finish(); + } +} +impl Debug for (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V) +where + A: Debug, + B: Debug, + C: Debug, + D: Debug, + E: Debug, + F: Debug, + G: Debug, + H: Debug, + I: Debug, + J: Debug, + K: Debug, + L: Debug, + M: Debug, + N: Debug, + O: Debug, + P: Debug, + Q: Debug, + R: Debug, + S: Debug, + T: Debug, + U: Debug, + V: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_tuple(""); + let mut f = f.field(self.0); + let mut f = f.field(self.1); + let mut f = f.field(self.2); + let mut f = f.field(self.3); + let mut f = f.field(self.4); + let mut f = f.field(self.5); + let mut f = f.field(self.6); + let mut f = f.field(self.7); + let mut f = f.field(self.8); + let mut f = f.field(self.9); + let mut f = f.field(self.10); + let mut f = f.field(self.11); + let mut f = f.field(self.12); + let mut f = f.field(self.13); + let mut f = f.field(self.14); + let mut f = f.field(self.15); + let mut f = f.field(self.16); + let mut f = f.field(self.17); + let mut f = f.field(self.18); + let mut f = f.field(self.19); + let mut f = f.field(self.20); + let mut f = f.field(self.21); + f.finish(); + } +} +impl Debug for (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W) +where + A: Debug, + B: Debug, + C: Debug, + D: Debug, + E: Debug, + F: Debug, + G: Debug, + H: Debug, + I: Debug, + J: Debug, + K: Debug, + L: Debug, + M: Debug, + N: Debug, + O: Debug, + P: Debug, + Q: Debug, + R: Debug, + S: Debug, + T: Debug, + U: Debug, + V: Debug, + W: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_tuple(""); + let mut f = f.field(self.0); + let mut f = f.field(self.1); + let mut f = f.field(self.2); + let mut f = f.field(self.3); + let mut f = f.field(self.4); + let mut f = f.field(self.5); + let mut f = f.field(self.6); + let mut f = f.field(self.7); + let mut f = f.field(self.8); + let mut f = f.field(self.9); + let mut f = f.field(self.10); + let mut f = f.field(self.11); + let mut f = f.field(self.12); + let mut f = f.field(self.13); + let mut f = f.field(self.14); + let mut f = f.field(self.15); + let mut f = f.field(self.16); + let mut f = f.field(self.17); + let mut f = f.field(self.18); + let mut f = f.field(self.19); + let mut f = f.field(self.20); + let mut f = f.field(self.21); + let mut f = f.field(self.22); + f.finish(); + } +} +impl Debug for (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X) +where + A: Debug, + B: Debug, + C: Debug, + D: Debug, + E: Debug, + F: Debug, + G: Debug, + H: Debug, + I: Debug, + J: Debug, + K: Debug, + L: Debug, + M: Debug, + N: Debug, + O: Debug, + P: Debug, + Q: Debug, + R: Debug, + S: Debug, + T: Debug, + U: Debug, + V: Debug, + W: Debug, + X: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_tuple(""); + let mut f = f.field(self.0); + let mut f = f.field(self.1); + let mut f = f.field(self.2); + let mut f = f.field(self.3); + let mut f = f.field(self.4); + let mut f = f.field(self.5); + let mut f = f.field(self.6); + let mut f = f.field(self.7); + let mut f = f.field(self.8); + let mut f = f.field(self.9); + let mut f = f.field(self.10); + let mut f = f.field(self.11); + let mut f = f.field(self.12); + let mut f = f.field(self.13); + let mut f = f.field(self.14); + let mut f = f.field(self.15); + let mut f = f.field(self.16); + let mut f = f.field(self.17); + let mut f = f.field(self.18); + let mut f = f.field(self.19); + let mut f = f.field(self.20); + let mut f = f.field(self.21); + let mut f = f.field(self.22); + let mut f = f.field(self.23); + f.finish(); + } +} +impl Debug for (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y) +where + A: Debug, + B: Debug, + C: Debug, + D: Debug, + E: Debug, + F: Debug, + G: Debug, + H: Debug, + I: Debug, + J: Debug, + K: Debug, + L: Debug, + M: Debug, + N: Debug, + O: Debug, + P: Debug, + Q: Debug, + R: Debug, + S: Debug, + T: Debug, + U: Debug, + V: Debug, + W: Debug, + X: Debug, + Y: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_tuple(""); + let mut f = f.field(self.0); + let mut f = f.field(self.1); + let mut f = f.field(self.2); + let mut f = f.field(self.3); + let mut f = f.field(self.4); + let mut f = f.field(self.5); + let mut f = f.field(self.6); + let mut f = f.field(self.7); + let mut f = f.field(self.8); + let mut f = f.field(self.9); + let mut f = f.field(self.10); + let mut f = f.field(self.11); + let mut f = f.field(self.12); + let mut f = f.field(self.13); + let mut f = f.field(self.14); + let mut f = f.field(self.15); + let mut f = f.field(self.16); + let mut f = f.field(self.17); + let mut f = f.field(self.18); + let mut f = f.field(self.19); + let mut f = f.field(self.20); + let mut f = f.field(self.21); + let mut f = f.field(self.22); + let mut f = f.field(self.23); + let mut f = f.field(self.24); + f.finish(); + } +} +impl Debug for (A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z) +where + A: Debug, + B: Debug, + C: Debug, + D: Debug, + E: Debug, + F: Debug, + G: Debug, + H: Debug, + I: Debug, + J: Debug, + K: Debug, + L: Debug, + M: Debug, + N: Debug, + O: Debug, + P: Debug, + Q: Debug, + R: Debug, + S: Debug, + T: Debug, + U: Debug, + V: Debug, + W: Debug, + X: Debug, + Y: Debug, + Z: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut f = f.debug_tuple(""); + let mut f = f.field(self.0); + let mut f = f.field(self.1); + let mut f = f.field(self.2); + let mut f = f.field(self.3); + let mut f = f.field(self.4); + let mut f = f.field(self.5); + let mut f = f.field(self.6); + let mut f = f.field(self.7); + let mut f = f.field(self.8); + let mut f = f.field(self.9); + let mut f = f.field(self.10); + let mut f = f.field(self.11); + let mut f = f.field(self.12); + let mut f = f.field(self.13); + let mut f = f.field(self.14); + let mut f = f.field(self.15); + let mut f = f.field(self.16); + let mut f = f.field(self.17); + let mut f = f.field(self.18); + let mut f = f.field(self.19); + let mut f = f.field(self.20); + let mut f = f.field(self.21); + let mut f = f.field(self.22); + let mut f = f.field(self.23); + let mut f = f.field(self.24); + let mut f = f.field(self.25); + f.finish(); + } +} +// END TUPLES_DEBUG diff --git a/sway-lib-std/src/lib.sw b/sway-lib-std/src/lib.sw index 6e13a36d99..68197febd6 100644 --- a/sway-lib-std/src/lib.sw +++ b/sway-lib-std/src/lib.sw @@ -9,6 +9,7 @@ pub mod ops; pub mod raw_ptr; pub mod raw_slice; pub mod codec; +pub mod debug; pub mod slice; pub mod constants; pub mod error_signals; diff --git a/sway-lib-std/src/prelude.sw b/sway-lib-std/src/prelude.sw index 14518dac37..dc049d663b 100644 --- a/sway-lib-std/src/prelude.sw +++ b/sway-lib-std/src/prelude.sw @@ -50,3 +50,4 @@ pub use ::codec::*; pub use ::str::*; #[cfg(experimental_error_type = true)] pub use ::marker::*; +pub use ::debug::*; diff --git a/sway-lib-std/src/vec.sw b/sway-lib-std/src/vec.sw index 79f65821cc..415b29df7d 100644 --- a/sway-lib-std/src/vec.sw +++ b/sway-lib-std/src/vec.sw @@ -10,6 +10,7 @@ use ::codec::*; use ::ops::*; use ::raw_slice::*; use ::clone::Clone; +use ::debug::{Debug, DebugList, Formatter}; struct RawVec { ptr: raw_ptr, @@ -905,3 +906,18 @@ where true } } + +impl Debug for Vec +where + T: Debug, +{ + fn fmt(self, ref mut f: Formatter) { + let mut l = f.debug_list(); + + for elem in self.iter() { + let _ = l.entry(elem); + } + + l.finish(); + } +} diff --git a/sway-lsp/src/core/session.rs b/sway-lsp/src/core/session.rs index 2712ed79a5..dbf3dda753 100644 --- a/sway-lsp/src/core/session.rs +++ b/sway-lsp/src/core/session.rs @@ -150,6 +150,7 @@ impl Session { let path = uri.to_file_path().unwrap(); let source_id = { engines.se().get_source_id(&path) }; engines.clear_module(&source_id); + Ok(()) } @@ -326,6 +327,7 @@ pub fn compile( retrigger_compilation, &[], &[sway_features::Feature::NewEncoding], + sway_core::DbgGeneration::None, ) .map_err(LanguageServerError::FailedToCompile) } diff --git a/sway-lsp/tests/fixtures/tokens/structs/src/main.sw b/sway-lsp/tests/fixtures/tokens/structs/src/main.sw index d4f1cc289b..67e0ceb528 100644 --- a/sway-lsp/tests/fixtures/tokens/structs/src/main.sw +++ b/sway-lsp/tests/fixtures/tokens/structs/src/main.sw @@ -17,6 +17,12 @@ struct MyStruct { o: Option, } +impl Debug for MyStruct { + fn fmt(self, f: Formatter) { + + } +} + struct Simple { x: u8, } diff --git a/sway-lsp/tests/lib.rs b/sway-lsp/tests/lib.rs index 0d5329d7a4..0849879d71 100644 --- a/sway-lsp/tests/lib.rs +++ b/sway-lsp/tests/lib.rs @@ -1239,6 +1239,7 @@ fn go_to_definition_for_functions() { } #[test] +#[ignore = "https://github.com/FuelLabs/sway/issues/7025"] fn go_to_definition_for_structs() { run_async!({ let server = ServerState::default(); diff --git a/sway-types/src/source_engine.rs b/sway-types/src/source_engine.rs index 04306cefcb..fc5d082f07 100644 --- a/sway-types/src/source_engine.rs +++ b/sway-types/src/source_engine.rs @@ -3,6 +3,7 @@ use parking_lot::RwLock; use std::{ collections::{BTreeSet, HashMap}, path::PathBuf, + str::FromStr, }; /// The Source Engine manages a relationship between file paths and their corresponding @@ -47,7 +48,10 @@ impl SourceEngine { } pub fn is_source_id_autogenerated(&self, source_id: &SourceId) -> bool { - self.get_path(source_id).starts_with("") + self.get_path(source_id) + .display() + .to_string() + .contains("") } /// This function retrieves an integer-based source ID for a provided path buffer. @@ -91,8 +95,20 @@ impl SourceEngine { source_id } - pub fn get_autogenerated_source_id(&self, program_id: ProgramId) -> SourceId { - self.get_source_id_with_program_id(&Self::AUTOGENERATED_PATH.into(), program_id) + /// Return the associated autogenerated pseudo file for the passed `source_id`. + /// Example: main.autogenerated.sw for main.sw + /// + /// Returns `None`, if `source_id` does not have a valid path. + pub fn get_associated_autogenerated_source_id(&self, source_id: &SourceId) -> Option { + let path = self.get_path(source_id); + let file_name = PathBuf::from_str(path.file_name()?.to_str()?).ok()?; + let path = path.with_file_name(format!( + "{}.{}.{}", + file_name.file_stem()?.to_str()?, + Self::AUTOGENERATED_PATH, + file_name.extension()?.to_str()? + )); + Some(self.get_source_id_with_program_id(&path, source_id.program_id())) } /// This function provides the file path corresponding to a specified source ID. diff --git a/test/src/e2e_vm_tests/harness.rs b/test/src/e2e_vm_tests/harness.rs index deb185d15d..ae0294a551 100644 --- a/test/src/e2e_vm_tests/harness.rs +++ b/test/src/e2e_vm_tests/harness.rs @@ -7,11 +7,12 @@ use forc_client::{ NodeTarget, }; use forc_pkg::{BuildProfile, Built, BuiltPackage, PrintOpts}; +use forc_test::ecal::EcalSyscallHandler; use fuel_tx::TransactionBuilder; +use fuel_vm::checked_transaction::builder::TransactionBuilderExt; use fuel_vm::fuel_tx::{self, consensus_parameters::ConsensusParametersV1}; use fuel_vm::interpreter::Interpreter; use fuel_vm::prelude::*; -use fuel_vm::{checked_transaction::builder::TransactionBuilderExt, interpreter::NotSupportedEcal}; use futures::Future; use rand::rngs::StdRng; use rand::{Rng, SeedableRng}; @@ -143,7 +144,7 @@ pub(crate) async fn runs_on_node( } pub(crate) enum VMExecutionResult { - Fuel(ProgramState, Vec), + Fuel(ProgramState, Vec, Box), Evm(revm::primitives::result::ExecutionResult), } @@ -207,13 +208,14 @@ pub(crate) fn runs_in_vm( .map_err(|e| anyhow::anyhow!("{e:?}"))?; let mem_instance = MemoryInstance::new(); - let mut i: Interpreter<_, _, _, NotSupportedEcal> = + let mut i: Interpreter<_, _, _, EcalSyscallHandler> = Interpreter::with_storage(mem_instance, storage, Default::default()); let transition = i.transact(tx).map_err(anyhow::Error::msg)?; Ok(VMExecutionResult::Fuel( *transition.state(), transition.receipts().to_vec(), + Box::new(i.ecal_state().clone()), )) } BuildTarget::EVM => { diff --git a/test/src/e2e_vm_tests/mod.rs b/test/src/e2e_vm_tests/mod.rs index ecccea6156..38a838212f 100644 --- a/test/src/e2e_vm_tests/mod.rs +++ b/test/src/e2e_vm_tests/mod.rs @@ -11,6 +11,7 @@ use colored::*; use core::fmt; use forc_pkg::manifest::{GenericManifestFile, ManifestFile}; use forc_pkg::BuildProfile; +use forc_test::ecal::Syscall; use forc_util::tx_utils::decode_log_data; use fuel_vm::fuel_tx; use fuel_vm::fuel_types::canonical::Input; @@ -404,8 +405,27 @@ impl TestContext { let result = harness::runs_in_vm(compiled.clone(), script_data, witness_data)?; let actual_result = match result { - harness::VMExecutionResult::Fuel(state, receipts) => { + harness::VMExecutionResult::Fuel(state, receipts, ecal) => { print_receipts(output, &receipts); + + use std::fmt::Write; + let _ = writeln!(output, " {}", "Captured Output".green().bold()); + for captured in ecal.captured.iter() { + match captured { + Syscall::Write { bytes, .. } => { + let s = std::str::from_utf8(bytes.as_slice()).unwrap(); + output.push_str(s); + } + Syscall::Unknown { ra, rb, rc, rd } => { + let _ = writeln!( + output, + "Unknown ecal: {} {} {} {}", + ra, rb, rc, rd + ); + } + } + } + match state { ProgramState::Return(v) => TestResult::Return(v), ProgramState::ReturnData(digest) => { diff --git a/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-assert/reduced_lib.config b/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-assert/reduced_lib.config index c35d772064..40f5d183fe 100644 --- a/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-assert/reduced_lib.config +++ b/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-assert/reduced_lib.config @@ -13,3 +13,4 @@ assert.sw error_signals.sw logging.sw revert.sw +debug.sw diff --git a/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-assert/src/lib.sw b/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-assert/src/lib.sw index dff041d3a6..38bd24f54a 100644 --- a/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-assert/src/lib.sw +++ b/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-assert/src/lib.sw @@ -16,3 +16,4 @@ pub mod logging; pub mod revert; pub mod assert; pub mod prelude; +pub mod debug; diff --git a/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-conversions/reduced_lib.config b/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-conversions/reduced_lib.config index e89ce8cdc8..e4c3f2cac5 100644 --- a/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-conversions/reduced_lib.config +++ b/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-conversions/reduced_lib.config @@ -45,4 +45,5 @@ primitive_conversions/u16.sw primitive_conversions/u32.sw primitive_conversions/u64.sw primitive_conversions/u256.sw -primitive_conversions/u8.sw \ No newline at end of file +primitive_conversions/u8.sw +debug.sw diff --git a/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-conversions/src/lib.sw b/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-conversions/src/lib.sw index 9bb89f0814..d621f7ee91 100644 --- a/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-conversions/src/lib.sw +++ b/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-conversions/src/lib.sw @@ -31,5 +31,6 @@ pub mod math; pub mod array_conversions; pub mod bytes_conversions; pub mod clone; +pub mod debug; pub mod prelude; diff --git a/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-core/reduced_lib.config b/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-core/reduced_lib.config index f91e5c84c8..5087306b77 100644 --- a/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-core/reduced_lib.config +++ b/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-core/reduced_lib.config @@ -9,3 +9,4 @@ raw_slice.sw codec.sw str.sw marker.sw +debug.sw diff --git a/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-core/src/lib.sw b/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-core/src/lib.sw index 7f9fc12b16..8b23325b39 100644 --- a/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-core/src/lib.sw +++ b/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-core/src/lib.sw @@ -11,5 +11,5 @@ pub mod raw_slice; pub mod codec; pub mod r#str; pub mod marker; - pub mod prelude; +pub mod debug; diff --git a/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-core/src/prelude.sw b/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-core/src/prelude.sw index 4548dc454e..ab5a37132d 100644 --- a/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-core/src/prelude.sw +++ b/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-core/src/prelude.sw @@ -14,3 +14,4 @@ pub use ::codec::*; pub use ::str::*; #[cfg(experimental_error_type = true)] pub use ::marker::*; +pub use ::debug::*; diff --git a/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-option-result/reduced_lib.config b/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-option-result/reduced_lib.config index 364663eb69..1fe2cbc975 100644 --- a/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-option-result/reduced_lib.config +++ b/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-option-result/reduced_lib.config @@ -15,3 +15,4 @@ logging.sw option.sw result.sw revert.sw +debug.sw diff --git a/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-option-result/src/lib.sw b/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-option-result/src/lib.sw index 257db6c0dc..f8c4571c66 100644 --- a/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-option-result/src/lib.sw +++ b/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-option-result/src/lib.sw @@ -18,3 +18,4 @@ pub mod result; pub mod option; pub mod assert; pub mod prelude; +pub mod debug; diff --git a/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-vec/reduced_lib.config b/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-vec/reduced_lib.config index 69d6fde1b5..54f8436cfc 100644 --- a/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-vec/reduced_lib.config +++ b/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-vec/reduced_lib.config @@ -20,3 +20,4 @@ vec.sw iterator.sw convert.sw clone.sw +debug.sw diff --git a/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-vec/src/lib.sw b/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-vec/src/lib.sw index 348ac0524c..89708b9866 100644 --- a/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-vec/src/lib.sw +++ b/test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-vec/src/lib.sw @@ -22,5 +22,6 @@ pub mod alloc; pub mod iterator; pub mod clone; pub mod vec; +pub mod debug; pub mod prelude; diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/dbg_wrong_args_count/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_fail/dbg_wrong_args_count/Forc.lock new file mode 100644 index 0000000000..1c22518178 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/dbg_wrong_args_count/Forc.lock @@ -0,0 +1,8 @@ +[[package]] +name = "dbg_wrong_args_count" +source = "member" +dependencies = ["std"] + +[[package]] +name = "std" +source = "path+from-root-7E04A7BB9FC18A6D" diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/dbg_wrong_args_count/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_fail/dbg_wrong_args_count/Forc.toml new file mode 100644 index 0000000000..ddca7cf336 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/dbg_wrong_args_count/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +entry = "main.sw" +license = "Apache-2.0" +name = "dbg_wrong_args_count" + +[dependencies] +std = { path = "../../../reduced_std_libs/sway-lib-std-core" } diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/dbg_wrong_args_count/snapshot.toml b/test/src/e2e_vm_tests/test_programs/should_fail/dbg_wrong_args_count/snapshot.toml new file mode 100644 index 0000000000..e69de29bb2 diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/dbg_wrong_args_count/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_fail/dbg_wrong_args_count/src/main.sw new file mode 100644 index 0000000000..052b3f96fc --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/dbg_wrong_args_count/src/main.sw @@ -0,0 +1,6 @@ +script; + +fn main() { + let _ = __dbg(); + let _ = __dbg(1, 2); +} diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/dbg_wrong_args_count/stdout.snap b/test/src/e2e_vm_tests/test_programs/should_fail/dbg_wrong_args_count/stdout.snap new file mode 100644 index 0000000000..70b77ec077 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_fail/dbg_wrong_args_count/stdout.snap @@ -0,0 +1,34 @@ +--- +source: test/src/snapshot/mod.rs +--- +> forc build --path test/src/e2e_vm_tests/test_programs/should_fail/dbg_wrong_args_count +exit status: 1 +output: + Building test/src/e2e_vm_tests/test_programs/should_fail/dbg_wrong_args_count + Compiling library std (test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-core) + Compiling script dbg_wrong_args_count (test/src/e2e_vm_tests/test_programs/should_fail/dbg_wrong_args_count) +error + --> test/src/e2e_vm_tests/test_programs/should_fail/dbg_wrong_args_count/src/main.sw:4:13 + | +2 | +3 | fn main() { +4 | let _ = __dbg(); + | ^^^^^^^ Call to "dbg" expects 1 argument(s) +5 | let _ = __dbg(1, 2); +6 | } + | +____ + +error + --> test/src/e2e_vm_tests/test_programs/should_fail/dbg_wrong_args_count/src/main.sw:5:13 + | +3 | fn main() { +4 | let _ = __dbg(); +5 | let _ = __dbg(1, 2); + | ^^^^^^^^^^^ Call to "dbg" expects 1 argument(s) +6 | } + | +____ + + Aborting due to 2 errors. +error: Failed to compile dbg_wrong_args_count diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/encode_append_wrong_args/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/encode_append_wrong_args/test.toml index 7c6ea4d2bd..1307b966b4 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/encode_append_wrong_args/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/encode_append_wrong_args/test.toml @@ -4,4 +4,4 @@ category = "fail" # check: $()buffer: __encode_buffer_append(buffer.buffer) # check: $()buffer: __encode_buffer_append(buffer.buffer) -# nextln: $()Call to "encode_buffer_append" expects 2 arguments +# nextln: $()Call to "encode_buffer_append" expects 2 argument(s) diff --git a/test/src/e2e_vm_tests/test_programs/should_fail/encode_buffer_empty_with_args/test.toml b/test/src/e2e_vm_tests/test_programs/should_fail/encode_buffer_empty_with_args/test.toml index e51812b07e..7f4be384e0 100644 --- a/test/src/e2e_vm_tests/test_programs/should_fail/encode_buffer_empty_with_args/test.toml +++ b/test/src/e2e_vm_tests/test_programs/should_fail/encode_buffer_empty_with_args/test.toml @@ -1,4 +1,4 @@ category = "fail" # check: $()buffer: __encode_buffer_empty(self), -# nextln: $()Call to "encode_buffer_empty" expects 0 arguments +# nextln: $()Call to "encode_buffer_empty" expects 0 argument(s) diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/attributes_deprecated/stdout.snap b/test/src/e2e_vm_tests/test_programs/should_pass/language/attributes_deprecated/stdout.snap index 119fd81022..eb89f9bdba 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/attributes_deprecated/stdout.snap +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/attributes_deprecated/stdout.snap @@ -1,6 +1,5 @@ --- source: test/src/snapshot/mod.rs -assertion_line: 125 --- > forc build --path test/src/e2e_vm_tests/test_programs/should_pass/language/attributes_deprecated --release exit status: 0 @@ -165,7 +164,7 @@ warning: Struct field is deprecated ____ warning: Function is deprecated - --> :5:15 + --> test/src/e2e_vm_tests/test_programs/should_pass/language/attributes_deprecated/src/main..sw:5:15 | ... 5 | let _result = __contract_entry_deprecated_to_be_abi_method(); @@ -174,7 +173,7 @@ warning: Function is deprecated ____ warning: Function is deprecated - --> :9:15 + --> test/src/e2e_vm_tests/test_programs/should_pass/language/attributes_deprecated/src/main..sw:9:15 | ... 9 | let _result = __contract_entry_deprecated_abi_provided_method(); diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_dedup_decode/stdout.snap b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_dedup_decode/stdout.snap index f7c238b4b8..6aea07d2cf 100644 --- a/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_dedup_decode/stdout.snap +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_dedup_decode/stdout.snap @@ -271,7 +271,7 @@ script { !39 = span !3 2804 2817 !40 = span !3 2857 2896 !41 = (!32 !33 !40) -!42 = "" +!42 = "test/src/e2e_vm_tests/test_programs/should_pass/language/configurable_dedup_decode/src/main..sw" !43 = span !42 0 125 !44 = fn_name_span !42 7 14 !45 = (!43 !44) diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/stdout.snap b/test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/stdout.snap new file mode 100644 index 0000000000..87f572dc45 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/stdout.snap @@ -0,0 +1,261 @@ +--- +source: test/src/snapshot/mod.rs +--- +> forc build --path test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated +exit status: 0 +output: + Building test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated + Compiling library std (test/src/e2e_vm_tests/reduced_std_libs/sway-lib-std-assert) + Compiling script deprecated (test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated) +warning + --> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main..sw:4:39 + | +2 | #[allow(dead_code)] +3 | fn abi_encode(self, buffer: Buffer) -> Buffer { +4 | let buffer = self.a.abi_encode(buffer); + | - deprecated struct field +5 | let buffer = self.b.abi_encode(buffer); +6 | + | +____ + +warning + --> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main..sw:4:21 + | +2 | #[allow(dead_code)] +3 | fn abi_decode(ref mut buffer: BufferReader) -> Self { +4 | Self { a: buffer.decode::(),b: buffer.decode::(), } + | ---- deprecated struct +5 | } +6 | } + | +____ + +warning + --> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main..sw:5:25 + | +3 | fn abi_decode(ref mut buffer: BufferReader) -> Self { +4 | let variant: u64 = buffer.decode::(); +5 | match variant { 0 => B::A, + | - deprecated enum +6 | 1 => B::B, +7 | _ => __revert(0), } + | +____ + +warning + --> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main..sw:6:9 + | +4 | let variant: u64 = buffer.decode::(); +5 | match variant { 0 => B::A, +6 | 1 => B::B, + | - deprecated enum +7 | _ => __revert(0), } +8 | + | +____ + +warning + --> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main..sw:6:9 + | +4 | let variant: u64 = buffer.decode::(); +5 | match variant { 0 => B::A, +6 | 1 => B::B, + | - deprecated enum variant +7 | _ => __revert(0), } +8 | + | +____ + +warning + --> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:33:17 + | +31 | // TODO: support for traits, abis and their methods +32 | pub fn main() { +33 | let mut a = A { a: 0, b: 0 }; + | - deprecated struct +34 | let mut a_ref = &a; +35 | (*a_ref).a = 1; + | +____ + +warning + --> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:35:14 + | +33 | let mut a = A { a: 0, b: 0 }; +34 | let mut a_ref = &a; +35 | (*a_ref).a = 1; + | - deprecated struct field +36 | let b = B::A; +37 | depr(a); + | +____ + +warning + --> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:36:16 + | +34 | let mut a_ref = &a; +35 | (*a_ref).a = 1; +36 | let b = B::A; + | - deprecated enum +37 | depr(a); +38 | depr(A { a: 0, b: 0 }); + | +____ + +warning + --> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:37:5 + | +35 | (*a_ref).a = 1; +36 | let b = B::A; +37 | depr(a); + | ---- deprecated function +38 | depr(A { a: 0, b: 0 }); +39 | depr_b(b); + | +____ + +warning + --> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:38:10 + | +36 | let b = B::A; +37 | depr(a); +38 | depr(A { a: 0, b: 0 }); + | - deprecated struct +39 | depr_b(b); +40 | depr_b(B::A); + | +____ + +warning + --> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:38:5 + | +36 | let b = B::A; +37 | depr(a); +38 | depr(A { a: 0, b: 0 }); + | ---- deprecated function +39 | depr_b(b); +40 | depr_b(B::A); + | +____ + +warning + --> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:39:5 + | +37 | depr(a); +38 | depr(A { a: 0, b: 0 }); +39 | depr_b(b); + | ------ deprecated function +40 | depr_b(B::A); +41 | fun(a); + | +____ + +warning + --> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:40:15 + | +38 | depr(A { a: 0, b: 0 }); +39 | depr_b(b); +40 | depr_b(B::A); + | - deprecated enum +41 | fun(a); +42 | fun(A { a: 0, b: 0 }); + | +____ + +warning + --> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:40:5 + | +38 | depr(A { a: 0, b: 0 }); +39 | depr_b(b); +40 | depr_b(B::A); + | ------ deprecated function +41 | fun(a); +42 | fun(A { a: 0, b: 0 }); + | +____ + +warning + --> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:42:9 + | +40 | depr_b(B::A); +41 | fun(a); +42 | fun(A { a: 0, b: 0 }); + | - deprecated struct +43 | let _ = a.a; +44 | let _ = a.b; + | +____ + +warning + --> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:43:15 + | +41 | fun(a); +42 | fun(A { a: 0, b: 0 }); +43 | let _ = a.a; + | - deprecated struct field +44 | let _ = a.b; +45 | let _ = B::A; + | +____ + +warning + --> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:45:16 + | +43 | let _ = a.a; +44 | let _ = a.b; +45 | let _ = B::A; + | - deprecated enum +46 | let _ = B::B; +47 | a.fun(); + | +____ + +warning + --> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:46:16 + | +44 | let _ = a.b; +45 | let _ = B::A; +46 | let _ = B::B; + | - deprecated enum +47 | a.fun(); +48 | } + | +____ + +warning + --> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:46:16 + | +44 | let _ = a.b; +45 | let _ = B::A; +46 | let _ = B::B; + | - deprecated enum variant +47 | a.fun(); +48 | } + | +____ + +warning + --> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:47:7 + | +45 | let _ = B::A; +46 | let _ = B::B; +47 | a.fun(); + | --- deprecated struct +48 | } + | +____ + +warning + --> test/src/e2e_vm_tests/test_programs/should_pass/language/deprecated/src/main.sw:47:7 + | +45 | let _ = B::A; +46 | let _ = B::B; +47 | a.fun(); + | --- deprecated function +48 | } + | +____ + + Compiled script "deprecated" with 21 warnings. + Finished debug [unoptimized + fuel] target(s) [312 B] in ??? diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg/Forc.lock new file mode 100644 index 0000000000..7839254333 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg/Forc.lock @@ -0,0 +1,8 @@ +[[package]] +name = "dbg" +source = "member" +dependencies = ["std"] + +[[package]] +name = "std" +source = "path+from-root-2AFF83EAEE091763" diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg/Forc.toml new file mode 100644 index 0000000000..d6a631850c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg/Forc.toml @@ -0,0 +1,8 @@ +[project] +authors = ["Fuel Labs "] +license = "Apache-2.0" +entry = "main.sw" +name = "dbg" + +[dependencies] +std = { path = "../../../../../reduced_std_libs/sway-lib-std-core" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg/snapshot.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg/snapshot.toml new file mode 100644 index 0000000000..fd9a5d0439 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg/snapshot.toml @@ -0,0 +1,4 @@ +cmds = [ + "forc build --path {root} --asm final | grep ecal", + "forc build --path {root} --release --asm final | grep ecal" +] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg/src/main.sw new file mode 100644 index 0000000000..ee8758b1b4 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg/src/main.sw @@ -0,0 +1,29 @@ +script; + +struct S {} +enum E { None: (), Some: S } + +fn main() -> u64 { + // Primitive types + let _ = __dbg(8u8); + let _ = __dbg(16u16); + let _ = __dbg(32u32); + let _ = __dbg(64u64); + let _ = __dbg(0x100u256); + + // strings + let _ = __dbg("Hello!"); + let _ = __dbg(__to_str_array("Hello!")); + + // Aggregates + let _ = __dbg((1u64, 2u64)); + let _ = __dbg([1u64, 2u64]); + + // Strucs and Enum + let _ = __dbg(S { }); + let _ = __dbg(E::None); + let _ = __dbg(E::Some(S { })); + + // should return its argument + __dbg(11u64) +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg/stdout.snap b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg/stdout.snap new file mode 100644 index 0000000000..da51ca3320 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg/stdout.snap @@ -0,0 +1,12 @@ +--- +source: test/src/snapshot/mod.rs +--- +> forc build --path test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg --asm final | grep ecal +ecal $r2 $r3 $r0 $r1 ; ecal id fd buf count +ecal $r3 $r0 $r1 $r2 ; ecal id fd buf count +ecal $r3 $r0 $r1 $r2 ; ecal id fd buf count +ecal $r3 $r0 $r1 $r2 ; ecal id fd buf count +ecal $r4 $r2 $r3 $r0 ; ecal id fd buf count +ecal $r3 $r0 $r1 $r2 ; ecal id fd buf count + +> forc build --path test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg --release --asm final | grep ecal diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg/test.toml new file mode 100644 index 0000000000..2d263f4382 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg/test.toml @@ -0,0 +1,4 @@ +category = "run" +expected_result = { action = "return", value = 1 } +expected_result_new_encoding = { action = "return_data", value = "000000000000000B" } +expected_warnings = 10 diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg_release/Forc.lock b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg_release/Forc.lock new file mode 100644 index 0000000000..10d129e379 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg_release/Forc.lock @@ -0,0 +1,8 @@ +[[package]] +name = "dbg_release" +source = "member" +dependencies = ["std"] + +[[package]] +name = "std" +source = "path+from-root-A3D53D8266747D89" diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg_release/Forc.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg_release/Forc.toml new file mode 100644 index 0000000000..10c6f236b5 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg_release/Forc.toml @@ -0,0 +1,9 @@ +[project] +authors = ["Fuel Labs "] +license = "Apache-2.0" +entry = "main.sw" +name = "dbg_release" +force-dbg-in-release = true + +[dependencies] +std = { path = "../../../../../reduced_std_libs/sway-lib-std-core" } diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg_release/snapshot.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg_release/snapshot.toml new file mode 100644 index 0000000000..fd9a5d0439 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg_release/snapshot.toml @@ -0,0 +1,4 @@ +cmds = [ + "forc build --path {root} --asm final | grep ecal", + "forc build --path {root} --release --asm final | grep ecal" +] diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg_release/src/main.sw b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg_release/src/main.sw new file mode 100644 index 0000000000..ee8758b1b4 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg_release/src/main.sw @@ -0,0 +1,29 @@ +script; + +struct S {} +enum E { None: (), Some: S } + +fn main() -> u64 { + // Primitive types + let _ = __dbg(8u8); + let _ = __dbg(16u16); + let _ = __dbg(32u32); + let _ = __dbg(64u64); + let _ = __dbg(0x100u256); + + // strings + let _ = __dbg("Hello!"); + let _ = __dbg(__to_str_array("Hello!")); + + // Aggregates + let _ = __dbg((1u64, 2u64)); + let _ = __dbg([1u64, 2u64]); + + // Strucs and Enum + let _ = __dbg(S { }); + let _ = __dbg(E::None); + let _ = __dbg(E::Some(S { })); + + // should return its argument + __dbg(11u64) +} \ No newline at end of file diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg_release/stdout.snap b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg_release/stdout.snap new file mode 100644 index 0000000000..ee644f41d9 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg_release/stdout.snap @@ -0,0 +1,16 @@ +--- +source: test/src/snapshot/mod.rs +--- +> forc build --path test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg_release --asm final | grep ecal +ecal $r2 $r3 $r0 $r1 ; ecal id fd buf count +ecal $r3 $r0 $r1 $r2 ; ecal id fd buf count +ecal $r3 $r0 $r1 $r2 ; ecal id fd buf count +ecal $r3 $r0 $r1 $r2 ; ecal id fd buf count +ecal $r4 $r2 $r3 $r0 ; ecal id fd buf count +ecal $r3 $r0 $r1 $r2 ; ecal id fd buf count + +> forc build --path test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg_release --release --asm final | grep ecal +ecal $r2 $r6 $r0 $r1 ; ecal id fd buf count +ecal $r7 $r8 $r3 $r6 ; ecal id fd buf count +ecal $r3 $r4 $r2 $r0 ; ecal id fd buf count +ecal $r3 $r4 $r1 $r2 ; ecal id fd buf count diff --git a/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg_release/test.toml b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg_release/test.toml new file mode 100644 index 0000000000..2d263f4382 --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/should_pass/language/intrinsics/dbg_release/test.toml @@ -0,0 +1,4 @@ +category = "run" +expected_result = { action = "return", value = 1 } +expected_result_new_encoding = { action = "return_data", value = "000000000000000B" } +expected_warnings = 10 diff --git a/test/src/ir_generation/mod.rs b/test/src/ir_generation/mod.rs index 30d2a329a1..b945d77af1 100644 --- a/test/src/ir_generation/mod.rs +++ b/test/src/ir_generation/mod.rs @@ -236,6 +236,7 @@ pub(super) async fn run( path.clone(), PathBuf::from("/"), build_target, + sway_core::DbgGeneration::Full, ); // Include unit tests in the build. diff --git a/test/src/reduced_std_libs.rs b/test/src/reduced_std_libs.rs index b5a5cfb6e1..f6edafae1c 100644 --- a/test/src/reduced_std_libs.rs +++ b/test/src/reduced_std_libs.rs @@ -13,7 +13,7 @@ const REDUCED_LIB_CONFIG_FILE_NAME: &str = "reduced_lib.config"; /// Creates the reduced versions of `std` libraries based on the list of /// modules defined in [REDUCED_LIB_CONFIG_FILE_NAME] file for each reduced library /// available in the [REDUCED_STD_LIBS_DIR_NAME]. -pub(crate) fn create() -> Result<()> { +pub fn create() -> Result<()> { let manifest_dir = env!("CARGO_MANIFEST_DIR"); let reduced_libs_dir = format!("{manifest_dir}/src/e2e_vm_tests/{REDUCED_STD_LIBS_DIR_NAME}"); let std_lib_src_dir = format!("{manifest_dir}/../sway-lib-std/src"); diff --git a/test/src/snapshot/mod.rs b/test/src/snapshot/mod.rs index 681b595616..c00a21f740 100644 --- a/test/src/snapshot/mod.rs +++ b/test/src/snapshot/mod.rs @@ -77,40 +77,60 @@ pub(super) async fn run(filter_regex: Option<®ex::Regex>) -> Result<()> { let _ = writeln!(&mut snapshot, "> {}", cmd); - // 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") - }; + let mut last_output: Option = None; - let o = duct::cmd!("bash", "-c", cmd.clone()) - .dir(repo_root.clone()) - .stderr_to_stdout() - .stdout_capture() - .env("COLUMNS", "10") - .unchecked() - .start() - .unwrap(); - let o = o.wait().unwrap(); + for cmd in cmd.split("|") { + let cmd = cmd.trim(); - let _ = writeln!( - &mut snapshot, - "{}", - clean_output(&format!( + // 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 if let Some(cmd) = cmd.strip_prefix("grep ") { + let arg = cmd.trim(); + if let Some(l) = last_output.take() { + let mut new_output = String::new(); + for line in l.lines() { + if line.contains(arg) { + new_output.push_str(line); + new_output.push('\n'); + } + } + last_output = Some(new_output); + } + continue; + } else { + panic!("Not supported. Possible commands: forc") + }; + + let o = duct::cmd!("bash", "-c", cmd.clone()) + .dir(repo_root.clone()) + .stderr_to_stdout() + .stdout_capture(); + + let o = if let Some(last_output) = last_output.as_ref() { + o.stdin_bytes(last_output.as_bytes()) + } else { + o + }; + + let o = o.env("COLUMNS", "10").unchecked().start().unwrap(); + let o = o.wait().unwrap(); + last_output = Some(clean_output(&format!( "exit status: {}\noutput:\n{}", o.status.code().unwrap(), std::str::from_utf8(&o.stdout).unwrap(), - )) - ); + ))); + } + + let _ = writeln!(&mut snapshot, "{}", last_output.unwrap_or_default()); } fn stdout(root: &str, snapshot: &str) {