diff --git a/.cargo/config.toml.in b/.cargo/config.toml.in index 7fd98731d576..fb3dccf2934c 100644 --- a/.cargo/config.toml.in +++ b/.cargo/config.toml.in @@ -40,9 +40,9 @@ git = "https://github.com/franziskuskiefer/cose-rust" rev = "43c22248d136c8b38fe42ea709d08da6355cf04b" replace-with = "vendored-sources" -[source."git+https://github.com/gfx-rs/wgpu?rev=c6286791febc64cf8ef054b5356c2669327ef51c"] +[source."git+https://github.com/gfx-rs/wgpu?rev=c7c79a0dc9356081a884b5518d1c08ce7a09c7c5"] git = "https://github.com/gfx-rs/wgpu" -rev = "c6286791febc64cf8ef054b5356c2669327ef51c" +rev = "c7c79a0dc9356081a884b5518d1c08ce7a09c7c5" replace-with = "vendored-sources" [source."git+https://github.com/glandium/rust-objc?rev=4de89f5aa9851ceca4d40e7ac1e2759410c04324"] diff --git a/Cargo.lock b/Cargo.lock index 897f216d31b0..7fcad110be1e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -642,6 +642,26 @@ version = "3.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa" +[[package]] +name = "bytemuck" +version = "1.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "byteorder" version = "1.5.0" @@ -929,12 +949,13 @@ dependencies = [ [[package]] name = "codespan-reporting" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" +checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" dependencies = [ + "serde", "termcolor", - "unicode-width 0.1.999", + "unicode-width 0.2.0", ] [[package]] @@ -4507,7 +4528,7 @@ checksum = "a2983372caf4480544083767bf2d27defafe32af49ab4df3a0b7fc90793a3664" [[package]] name = "naga" version = "24.0.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=c6286791febc64cf8ef054b5356c2669327ef51c#c6286791febc64cf8ef054b5356c2669327ef51c" +source = "git+https://github.com/gfx-rs/wgpu?rev=c7c79a0dc9356081a884b5518d1c08ce7a09c7c5#c7c79a0dc9356081a884b5518d1c08ce7a09c7c5" dependencies = [ "arrayvec", "bit-set", @@ -4524,7 +4545,6 @@ dependencies = [ "serde", "spirv", "strum", - "termcolor", "thiserror 2.0.9", "unicode-ident", ] @@ -5563,14 +5583,15 @@ dependencies = [ [[package]] name = "ron" -version = "0.8.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" +checksum = "63f3aa105dea217ef30d89581b65a4d527a19afc95ef5750be3890e8d3c5b837" dependencies = [ - "base64 0.21.999", + "base64 0.22.1", "bitflags 2.9.0", "serde", "serde_derive", + "unicode-ident", ] [[package]] @@ -7387,11 +7408,13 @@ dependencies = [ [[package]] name = "wgpu-core" version = "24.0.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=c6286791febc64cf8ef054b5356c2669327ef51c#c6286791febc64cf8ef054b5356c2669327ef51c" +source = "git+https://github.com/gfx-rs/wgpu?rev=c7c79a0dc9356081a884b5518d1c08ce7a09c7c5#c7c79a0dc9356081a884b5518d1c08ce7a09c7c5" dependencies = [ "arrayvec", + "bit-set", "bit-vec", "bitflags 2.9.0", + "bytemuck", "cfg_aliases", "document-features", "hashbrown 0.14.5", @@ -7415,7 +7438,7 @@ dependencies = [ [[package]] name = "wgpu-core-deps-apple" version = "24.0.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=c6286791febc64cf8ef054b5356c2669327ef51c#c6286791febc64cf8ef054b5356c2669327ef51c" +source = "git+https://github.com/gfx-rs/wgpu?rev=c7c79a0dc9356081a884b5518d1c08ce7a09c7c5#c7c79a0dc9356081a884b5518d1c08ce7a09c7c5" dependencies = [ "wgpu-hal", ] @@ -7423,7 +7446,7 @@ dependencies = [ [[package]] name = "wgpu-core-deps-windows-linux-android" version = "24.0.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=c6286791febc64cf8ef054b5356c2669327ef51c#c6286791febc64cf8ef054b5356c2669327ef51c" +source = "git+https://github.com/gfx-rs/wgpu?rev=c7c79a0dc9356081a884b5518d1c08ce7a09c7c5#c7c79a0dc9356081a884b5518d1c08ce7a09c7c5" dependencies = [ "wgpu-hal", ] @@ -7431,7 +7454,7 @@ dependencies = [ [[package]] name = "wgpu-hal" version = "24.0.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=c6286791febc64cf8ef054b5356c2669327ef51c#c6286791febc64cf8ef054b5356c2669327ef51c" +source = "git+https://github.com/gfx-rs/wgpu?rev=c7c79a0dc9356081a884b5518d1c08ce7a09c7c5#c7c79a0dc9356081a884b5518d1c08ce7a09c7c5" dependencies = [ "android_system_properties", "arrayvec", @@ -7439,6 +7462,7 @@ dependencies = [ "bit-set", "bitflags 2.9.0", "block", + "bytemuck", "cfg_aliases", "core-graphics-types 0.1.999", "gpu-alloc", @@ -7466,9 +7490,10 @@ dependencies = [ [[package]] name = "wgpu-types" version = "24.0.0" -source = "git+https://github.com/gfx-rs/wgpu?rev=c6286791febc64cf8ef054b5356c2669327ef51c#c6286791febc64cf8ef054b5356c2669327ef51c" +source = "git+https://github.com/gfx-rs/wgpu?rev=c7c79a0dc9356081a884b5518d1c08ce7a09c7c5#c7c79a0dc9356081a884b5518d1c08ce7a09c7c5" dependencies = [ "bitflags 2.9.0", + "bytemuck", "js-sys", "log", "serde", diff --git a/gfx/wgpu_bindings/Cargo.toml b/gfx/wgpu_bindings/Cargo.toml index 560b1b525b27..d4ecc00d73b6 100644 --- a/gfx/wgpu_bindings/Cargo.toml +++ b/gfx/wgpu_bindings/Cargo.toml @@ -17,7 +17,7 @@ default = [] [dependencies.wgc] package = "wgpu-core" git = "https://github.com/gfx-rs/wgpu" -rev = "c6286791febc64cf8ef054b5356c2669327ef51c" +rev = "c7c79a0dc9356081a884b5518d1c08ce7a09c7c5" # TODO: remove the replay feature on the next update containing https://github.com/gfx-rs/wgpu/pull/5182 features = [ "serde", @@ -26,7 +26,6 @@ features = [ "strict_asserts", "wgsl", "api_log_info", - "indirect-validation", ] # We want the wgpu-core Metal backend on macOS and iOS. @@ -34,32 +33,32 @@ features = [ [target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies.wgc] package = "wgpu-core" git = "https://github.com/gfx-rs/wgpu" -rev = "c6286791febc64cf8ef054b5356c2669327ef51c" +rev = "c7c79a0dc9356081a884b5518d1c08ce7a09c7c5" features = ["metal"] # We want the wgpu-core Direct3D backends on Windows. [target.'cfg(windows)'.dependencies.wgc] package = "wgpu-core" git = "https://github.com/gfx-rs/wgpu" -rev = "c6286791febc64cf8ef054b5356c2669327ef51c" +rev = "c7c79a0dc9356081a884b5518d1c08ce7a09c7c5" features = ["dx12"] # We want the wgpu-core Vulkan backend on Linux and Windows. [target.'cfg(any(windows, all(unix, not(any(target_os = "macos", target_os = "ios")))))'.dependencies.wgc] package = "wgpu-core" git = "https://github.com/gfx-rs/wgpu" -rev = "c6286791febc64cf8ef054b5356c2669327ef51c" +rev = "c7c79a0dc9356081a884b5518d1c08ce7a09c7c5" features = ["vulkan"] [dependencies.wgt] package = "wgpu-types" git = "https://github.com/gfx-rs/wgpu" -rev = "c6286791febc64cf8ef054b5356c2669327ef51c" +rev = "c7c79a0dc9356081a884b5518d1c08ce7a09c7c5" [dependencies.wgh] package = "wgpu-hal" git = "https://github.com/gfx-rs/wgpu" -rev = "c6286791febc64cf8ef054b5356c2669327ef51c" +rev = "c7c79a0dc9356081a884b5518d1c08ce7a09c7c5" features = ["oom_panic", "device_lost_panic", "internal_error_panic"] [target.'cfg(windows)'.dependencies] diff --git a/gfx/wgpu_bindings/moz.yaml b/gfx/wgpu_bindings/moz.yaml index 8f42b644564a..21aba70a2af9 100644 --- a/gfx/wgpu_bindings/moz.yaml +++ b/gfx/wgpu_bindings/moz.yaml @@ -8,8 +8,8 @@ origin: name: wgpu description: A cross-platform pure-Rust graphics API, modeled on the WebGPU standard url: https://github.com/gfx-rs/wgpu - release: c6286791febc64cf8ef054b5356c2669327ef51c (2025-03-19T16:26:50Z). - revision: c6286791febc64cf8ef054b5356c2669327ef51c + release: c7c79a0dc9356081a884b5518d1c08ce7a09c7c5 (2025-03-28T10:32:06Z). + revision: c7c79a0dc9356081a884b5518d1c08ce7a09c7c5 license: ['MIT', 'Apache-2.0'] updatebot: diff --git a/supply-chain/audits.toml b/supply-chain/audits.toml index 3f1cb265c12d..6769af4173a3 100644 --- a/supply-chain/audits.toml +++ b/supply-chain/audits.toml @@ -3683,7 +3683,7 @@ who = [ "Erich Gubler ", ] criteria = "safe-to-deploy" -delta = "24.0.0 -> 24.0.0@git:c6286791febc64cf8ef054b5356c2669327ef51c" +delta = "24.0.0 -> 24.0.0@git:c7c79a0dc9356081a884b5518d1c08ce7a09c7c5" importable = false [[audits.net2]] @@ -5922,7 +5922,7 @@ who = [ "Erich Gubler ", ] criteria = "safe-to-deploy" -delta = "24.0.0 -> 24.0.0@git:c6286791febc64cf8ef054b5356c2669327ef51c" +delta = "24.0.0 -> 24.0.0@git:c7c79a0dc9356081a884b5518d1c08ce7a09c7c5" importable = false [[audits.wgpu-hal]] @@ -6009,7 +6009,7 @@ who = [ "Erich Gubler ", ] criteria = "safe-to-deploy" -delta = "24.0.0 -> 24.0.0@git:c6286791febc64cf8ef054b5356c2669327ef51c" +delta = "24.0.0 -> 24.0.0@git:c7c79a0dc9356081a884b5518d1c08ce7a09c7c5" importable = false [[audits.wgpu-types]] @@ -6091,7 +6091,7 @@ who = [ "Erich Gubler ", ] criteria = "safe-to-deploy" -delta = "24.0.0 -> 24.0.0@git:c6286791febc64cf8ef054b5356c2669327ef51c" +delta = "24.0.0 -> 24.0.0@git:c7c79a0dc9356081a884b5518d1c08ce7a09c7c5" importable = false [[audits.whatsys]] diff --git a/supply-chain/imports.lock b/supply-chain/imports.lock index e32957b20d56..b6535bdabbd9 100644 --- a/supply-chain/imports.lock +++ b/supply-chain/imports.lock @@ -1454,6 +1454,131 @@ criteria = "safe-to-deploy" version = "0.3.7" aggregated-from = "https://chromium.googlesource.com/chromiumos/third_party/rust_crates/+/refs/heads/main/cargo-vet/audits.toml?format=TEXT" +[[audits.google.audits.bytemuck]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +version = "1.16.3" +notes = """ +Review notes from the original audit (of 1.14.3) may be found in +https://crrev.com/c/5362675. Note that this audit has initially missed UB risk +that was fixed in 1.16.2 - see https://github.com/Lokathor/bytemuck/pull/258. +Because of this, the original audit has been edited to certify version `1.16.3` +instead (see also https://crrev.com/c/5771867). +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bytemuck]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "1.16.3 -> 1.17.1" +notes = "Unsafe review comments can be found in https://crrev.com/c/5813463" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bytemuck]] +who = "Adrian Taylor " +criteria = "safe-to-deploy" +delta = "1.17.1 -> 1.18.0" +notes = "No code changes - just altering feature flag arrangements" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bytemuck]] +who = "Adrian Taylor " +criteria = "safe-to-deploy" +delta = "1.18.0 -> 1.19.0" +notes = "No code changes - just comment changes and adding the track_caller attribute." +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bytemuck]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "1.19.0 -> 1.20.0" +notes = "`unsafe` review can be found at https://crrev.com/c/6096767" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bytemuck]] +who = "Adrian Taylor " +criteria = "safe-to-deploy" +delta = "1.20.0 -> 1.21.0" +notes = "Unsafe review at https://chromium-review.googlesource.com/c/chromium/src/+/6111154/" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bytemuck]] +who = "Daniel Cheng " +criteria = "safe-to-deploy" +delta = "1.21.0 -> 1.22.0" +notes = """ +This adds new instances of unsafe, but the uses are justified: +- BoxBytes is essentially a Box<[u8], which is Send + Sync, so also marking BoxBytes as Send + Sync is justified. +- core::num::Saturating meets the criteria for Zeroable + Pod, so marking it as such is justified. + +See https://crrev.com/c/6321863 for more audit notes. +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bytemuck_derive]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +version = "1.6.0" +notes = """ +Grepped for \"unsafe\", \"crypt\", \"cipher\", \"fs\", \"net\" - there were no +hits except for 8 occurrences of `unsafe`. Additional `unsafe` review comments +can be found in https://crrev.com/c/5445719. +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bytemuck_derive]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "1.6.0 -> 1.6.1" +notes = """ +No behavior/code changes AFAICT - only adding +`#[allow(clippy::multiple_bound_locations)]`, doc comments, and making +some cosmetic changes in non-`.rs` files. +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bytemuck_derive]] +who = "danakj " +criteria = "safe-to-deploy" +delta = "1.6.1 -> 1.7.0" +notes = """ +Added support for Zeroable enums, which requires them to be represented as an integer and to have 0 as one of their values. + +Other trivial/formatting changes. +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bytemuck_derive]] +who = "Lukasz Anforowicz " +criteria = "safe-to-deploy" +delta = "1.7.0 -> 1.7.1" +notes = """ +No impact on safety AFAICT - the delta only specifies a new attribute for +`proc_macro_derive` to work around re-export issues described at +https://github.com/Lokathor/bytemuck/issues/159 +""" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bytemuck_derive]] +who = "danakj " +criteria = "safe-to-deploy" +delta = "1.7.1 -> 1.8.0" +notes = "Unsafe review: https://crrev.com/c/5921014" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bytemuck_derive]] +who = "Adrian Taylor " +criteria = "safe-to-deploy" +delta = "1.8.0 -> 1.8.1" +notes = "Changes do not impact safety." +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + +[[audits.google.audits.bytemuck_derive]] +who = "Chris Palmer " +criteria = "safe-to-deploy" +delta = "1.8.1 -> 1.9.2" +aggregated-from = "https://chromium.googlesource.com/chromium/src/+/main/third_party/rust/chromium_crates_io/supply-chain/audits.toml?format=TEXT" + [[audits.google.audits.clap_builder]] who = "Lukasz Anforowicz " criteria = "safe-to-deploy" diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/operation/compute/basic/dedicated.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/operation/compute/basic/dedicated.https.html.ini index dca26635d9be..a97d558a2913 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/operation/compute/basic/dedicated.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/operation/compute/basic/dedicated.https.html.ini @@ -3,38 +3,30 @@ implementation-status: if os == "win": backlog if os == "linux": backlog - expected: - if os == "win" and debug: CRASH [:dispatchSize="maximum"] expected: - if os == "win" and debug: [TIMEOUT, NOTRUN] - if os == "win" and not debug: FAIL + if os == "win": FAIL if os == "linux": FAIL [:dispatchSize=2048] expected: - if os == "win" and debug: [TIMEOUT, NOTRUN] - if os == "win" and not debug: FAIL + if os == "win": FAIL [:dispatchSize=2179] expected: - if os == "win" and debug: [TIMEOUT, NOTRUN] - if os == "win" and not debug: FAIL + if os == "win": FAIL [:dispatchSize=256] expected: - if os == "win" and debug: [TIMEOUT, NOTRUN] - if os == "win" and not debug: FAIL + if os == "win": FAIL [:dispatchSize=315] expected: - if os == "win" and debug: [TIMEOUT, NOTRUN] - if os == "win" and not debug: FAIL + if os == "win": FAIL [:dispatchSize=628] expected: - if os == "win" and debug: [TIMEOUT, NOTRUN] - if os == "win" and not debug: FAIL + if os == "win": FAIL [dedicated.https.html?worker=dedicated&q=webgpu:api,operation,compute,basic:memcpy:*] diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/operation/resource_init/texture_zero/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/operation/resource_init/texture_zero/cts.https.html.ini index d29206520a68..11bb645c954c 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/operation/resource_init/texture_zero/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/operation/resource_init/texture_zero/cts.https.html.ini @@ -490,8 +490,7 @@ [:dimension="2d";readMethod="CopyToTexture";format="r8unorm"] expected: - if os == "win" and debug: FAIL - if os == "win" and not debug: [PASS, FAIL] + if os == "win": [PASS, FAIL] if os == "linux": FAIL [:dimension="2d";readMethod="CopyToTexture";format="rg11b10ufloat"] diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxBindGroupsPlusVertexBuffers/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxBindGroupsPlusVertexBuffers/cts.https.html.ini index c73d895d601e..6bc256c2b268 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxBindGroupsPlusVertexBuffers/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxBindGroupsPlusVertexBuffers/cts.https.html.ini @@ -96,8 +96,7 @@ [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";encoderType="render"] expected: - if os == "win" and debug: [FAIL, TIMEOUT, NOTRUN] - if os == "win" and not debug: FAIL + if os == "win": [FAIL, TIMEOUT, NOTRUN] if os == "linux": [FAIL, TIMEOUT, NOTRUN] if os == "mac": FAIL @@ -158,5 +157,5 @@ [:limitTest="underDefault";testValueName="overLimit";encoderType="renderBundle"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxColorAttachmentBytesPerSample/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxColorAttachmentBytesPerSample/cts.https.html.ini index d55351f7674d..11015f94ea38 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxColorAttachmentBytesPerSample/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxColorAttachmentBytesPerSample/cts.https.html.ini @@ -555,6 +555,7 @@ [:limitTest="atMaximum";testValueName="atLimit";sampleCount=1;interleaveFormat="rg16uint"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] @@ -563,6 +564,7 @@ [:limitTest="atMaximum";testValueName="atLimit";sampleCount=1;interleaveFormat="rgba16uint"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] @@ -574,6 +576,7 @@ [:limitTest="atMaximum";testValueName="atLimit";sampleCount=4;interleaveFormat="rg16uint"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] @@ -582,6 +585,7 @@ [:limitTest="atMaximum";testValueName="atLimit";sampleCount=4;interleaveFormat="rgba16uint"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] @@ -593,7 +597,8 @@ [:limitTest="atMaximum";testValueName="overLimit";sampleCount=1;interleaveFormat="rg16uint"] expected: - if os == "win": FAIL + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] if os == "linux": [FAIL, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] @@ -602,7 +607,8 @@ [:limitTest="atMaximum";testValueName="overLimit";sampleCount=1;interleaveFormat="rgba16uint"] expected: - if os == "win": FAIL + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] if os == "linux": [FAIL, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] @@ -614,7 +620,8 @@ [:limitTest="atMaximum";testValueName="overLimit";sampleCount=4;interleaveFormat="rg16uint"] expected: - if os == "win": FAIL + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] if os == "linux": [FAIL, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] @@ -623,7 +630,8 @@ [:limitTest="atMaximum";testValueName="overLimit";sampleCount=4;interleaveFormat="rgba16uint"] expected: - if os == "win": FAIL + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] if os == "linux": [FAIL, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] @@ -638,6 +646,7 @@ [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";sampleCount=1;interleaveFormat="rg16uint"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] @@ -649,6 +658,7 @@ [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";sampleCount=1;interleaveFormat="rgba16uint"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] @@ -663,6 +673,7 @@ [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";sampleCount=4;interleaveFormat="rg16uint"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] @@ -671,6 +682,7 @@ [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";sampleCount=4;interleaveFormat="rgba16uint"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] @@ -682,7 +694,8 @@ [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";sampleCount=1;interleaveFormat="rg16uint"] expected: - if os == "win": FAIL + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] if os == "linux": [FAIL, TIMEOUT, NOTRUN] if os == "mac" and debug: FAIL if os == "mac" and not debug: [FAIL, TIMEOUT, NOTRUN] @@ -692,7 +705,8 @@ [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";sampleCount=1;interleaveFormat="rgba16uint"] expected: - if os == "win": FAIL + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] if os == "linux": [FAIL, TIMEOUT, NOTRUN] if os == "mac" and debug: FAIL if os == "mac" and not debug: [FAIL, TIMEOUT, NOTRUN] @@ -705,7 +719,8 @@ [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";sampleCount=4;interleaveFormat="rg16uint"] expected: - if os == "win": FAIL + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] if os == "linux": [FAIL, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] @@ -714,7 +729,8 @@ [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";sampleCount=4;interleaveFormat="rgba16uint"] expected: - if os == "win": FAIL + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] if os == "linux": [FAIL, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] @@ -726,6 +742,7 @@ [:limitTest="overMaximum";testValueName="atLimit";sampleCount=1;interleaveFormat="rg16uint"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] @@ -734,6 +751,7 @@ [:limitTest="overMaximum";testValueName="atLimit";sampleCount=1;interleaveFormat="rgba16uint"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] @@ -745,6 +763,7 @@ [:limitTest="overMaximum";testValueName="atLimit";sampleCount=4;interleaveFormat="rg16uint"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] @@ -753,6 +772,7 @@ [:limitTest="overMaximum";testValueName="atLimit";sampleCount=4;interleaveFormat="rgba16uint"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] @@ -764,6 +784,7 @@ [:limitTest="overMaximum";testValueName="overLimit";sampleCount=1;interleaveFormat="rg16uint"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] @@ -772,6 +793,7 @@ [:limitTest="overMaximum";testValueName="overLimit";sampleCount=1;interleaveFormat="rgba16uint"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] @@ -783,6 +805,7 @@ [:limitTest="overMaximum";testValueName="overLimit";sampleCount=4;interleaveFormat="rg16uint"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] @@ -791,6 +814,7 @@ [:limitTest="overMaximum";testValueName="overLimit";sampleCount=4;interleaveFormat="rgba16uint"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] @@ -799,50 +823,60 @@ [:limitTest="underDefault";testValueName="atLimit";sampleCount=1;interleaveFormat="r8unorm"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";sampleCount=1;interleaveFormat="rg16uint"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";sampleCount=1;interleaveFormat="rg8unorm"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";sampleCount=1;interleaveFormat="rgba16uint"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux" and debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";sampleCount=1;interleaveFormat="rgba8unorm"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";sampleCount=4;interleaveFormat="r8unorm"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";sampleCount=4;interleaveFormat="rg16uint"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";sampleCount=4;interleaveFormat="rg8unorm"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";sampleCount=4;interleaveFormat="rgba16uint"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";sampleCount=4;interleaveFormat="rgba8unorm"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] @@ -855,7 +889,8 @@ [:limitTest="underDefault";testValueName="overLimit";sampleCount=1;interleaveFormat="rg16uint"] expected: - if os == "win": FAIL + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] if os == "linux": [FAIL, TIMEOUT, NOTRUN] if os == "mac" and debug: FAIL if os == "mac" and not debug: [FAIL, TIMEOUT, NOTRUN] @@ -869,7 +904,8 @@ [:limitTest="underDefault";testValueName="overLimit";sampleCount=1;interleaveFormat="rgba16uint"] expected: - if os == "win": FAIL + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] if os == "linux": [FAIL, TIMEOUT, NOTRUN] if os == "mac" and debug: FAIL if os == "mac" and not debug: [FAIL, TIMEOUT, NOTRUN] @@ -890,7 +926,8 @@ [:limitTest="underDefault";testValueName="overLimit";sampleCount=4;interleaveFormat="rg16uint"] expected: - if os == "win": FAIL + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] if os == "linux": [FAIL, TIMEOUT, NOTRUN] if os == "mac" and debug: FAIL if os == "mac" and not debug: [FAIL, TIMEOUT, NOTRUN] @@ -904,7 +941,8 @@ [:limitTest="underDefault";testValueName="overLimit";sampleCount=4;interleaveFormat="rgba16uint"] expected: - if os == "win": FAIL + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] if os == "linux": [FAIL, TIMEOUT, NOTRUN] if os == "mac" and debug: FAIL if os == "mac" and not debug: [FAIL, TIMEOUT, NOTRUN] diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxComputeWorkgroupStorageSize/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxComputeWorkgroupStorageSize/cts.https.html.ini index eee500bdf1e8..7b017996ac93 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxComputeWorkgroupStorageSize/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxComputeWorkgroupStorageSize/cts.https.html.ini @@ -352,7 +352,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atDefault";testValueName="overLimit";async=false;wgslType="f32"] expected: @@ -372,7 +372,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atDefault";testValueName="overLimit";async=false;wgslType="mat2x2%3Cf32%3E"] expected: @@ -385,7 +385,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atDefault";testValueName="overLimit";async=false;wgslType="mat2x3%3Cf32%3E"] expected: @@ -398,7 +398,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atDefault";testValueName="overLimit";async=false;wgslType="mat2x4%3Cf32%3E"] expected: @@ -411,7 +411,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atDefault";testValueName="overLimit";async=false;wgslType="mat3x2%3Cf32%3E"] expected: @@ -424,7 +424,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atDefault";testValueName="overLimit";async=false;wgslType="mat3x3%3Cf32%3E"] expected: @@ -437,7 +437,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atDefault";testValueName="overLimit";async=false;wgslType="mat3x4%3Cf32%3E"] expected: @@ -450,7 +450,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atDefault";testValueName="overLimit";async=false;wgslType="mat4x2%3Cf32%3E"] expected: @@ -463,7 +463,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atDefault";testValueName="overLimit";async=false;wgslType="mat4x3%3Cf32%3E"] expected: @@ -476,7 +476,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atDefault";testValueName="overLimit";async=false;wgslType="mat4x4%3Cf32%3E"] expected: @@ -496,7 +496,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atDefault";testValueName="overLimit";async=false;wgslType="vec2%3Cf32%3E"] expected: @@ -523,7 +523,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atDefault";testValueName="overLimit";async=false;wgslType="vec3%3Cf32%3E"] expected: @@ -550,7 +550,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atDefault";testValueName="overLimit";async=false;wgslType="vec4%3Cf32%3E"] expected: @@ -595,7 +595,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atDefault";testValueName="overLimit";async=true;wgslType="f32"] expected: @@ -613,7 +613,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atDefault";testValueName="overLimit";async=true;wgslType="mat2x2%3Cf32%3E"] expected: @@ -625,7 +625,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atDefault";testValueName="overLimit";async=true;wgslType="mat2x3%3Cf32%3E"] expected: @@ -637,7 +637,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atDefault";testValueName="overLimit";async=true;wgslType="mat2x4%3Cf32%3E"] expected: @@ -649,7 +649,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atDefault";testValueName="overLimit";async=true;wgslType="mat3x2%3Cf32%3E"] expected: @@ -661,7 +661,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atDefault";testValueName="overLimit";async=true;wgslType="mat3x3%3Cf32%3E"] expected: @@ -673,7 +673,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atDefault";testValueName="overLimit";async=true;wgslType="mat3x4%3Cf32%3E"] expected: @@ -685,7 +685,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atDefault";testValueName="overLimit";async=true;wgslType="mat4x2%3Cf32%3E"] expected: @@ -697,7 +697,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atDefault";testValueName="overLimit";async=true;wgslType="mat4x3%3Cf32%3E"] expected: @@ -709,7 +709,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atDefault";testValueName="overLimit";async=true;wgslType="mat4x4%3Cf32%3E"] expected: @@ -727,7 +727,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atDefault";testValueName="overLimit";async=true;wgslType="vec2%3Cf32%3E"] expected: @@ -751,7 +751,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atDefault";testValueName="overLimit";async=true;wgslType="vec3%3Cf32%3E"] expected: @@ -775,7 +775,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atDefault";testValueName="overLimit";async=true;wgslType="vec4%3Cf32%3E"] expected: @@ -1081,8 +1081,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atMaximum";testValueName="overLimit";async=false;wgslType="f32"] expected: @@ -1102,8 +1101,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atMaximum";testValueName="overLimit";async=false;wgslType="mat2x2%3Cf32%3E"] expected: @@ -1116,8 +1114,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atMaximum";testValueName="overLimit";async=false;wgslType="mat2x3%3Cf32%3E"] expected: @@ -1130,8 +1127,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atMaximum";testValueName="overLimit";async=false;wgslType="mat2x4%3Cf32%3E"] expected: @@ -1144,8 +1140,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atMaximum";testValueName="overLimit";async=false;wgslType="mat3x2%3Cf32%3E"] expected: @@ -1158,8 +1153,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atMaximum";testValueName="overLimit";async=false;wgslType="mat3x3%3Cf32%3E"] expected: @@ -1172,8 +1166,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atMaximum";testValueName="overLimit";async=false;wgslType="mat3x4%3Cf32%3E"] expected: @@ -1186,8 +1179,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atMaximum";testValueName="overLimit";async=false;wgslType="mat4x2%3Cf32%3E"] expected: @@ -1200,8 +1192,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atMaximum";testValueName="overLimit";async=false;wgslType="mat4x3%3Cf32%3E"] expected: @@ -1214,8 +1205,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atMaximum";testValueName="overLimit";async=false;wgslType="mat4x4%3Cf32%3E"] expected: @@ -1235,8 +1225,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atMaximum";testValueName="overLimit";async=false;wgslType="vec2%3Cf32%3E"] expected: @@ -1263,8 +1252,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atMaximum";testValueName="overLimit";async=false;wgslType="vec3%3Cf32%3E"] expected: @@ -1291,8 +1279,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atMaximum";testValueName="overLimit";async=false;wgslType="vec4%3Cf32%3E"] expected: @@ -1340,8 +1327,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atMaximum";testValueName="overLimit";async=true;wgslType="f32"] expected: @@ -1361,8 +1347,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atMaximum";testValueName="overLimit";async=true;wgslType="mat2x2%3Cf32%3E"] expected: @@ -1375,8 +1360,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atMaximum";testValueName="overLimit";async=true;wgslType="mat2x3%3Cf32%3E"] expected: @@ -1389,8 +1373,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atMaximum";testValueName="overLimit";async=true;wgslType="mat2x4%3Cf32%3E"] expected: @@ -1403,8 +1386,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atMaximum";testValueName="overLimit";async=true;wgslType="mat3x2%3Cf32%3E"] expected: @@ -1417,8 +1399,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atMaximum";testValueName="overLimit";async=true;wgslType="mat3x3%3Cf32%3E"] expected: @@ -1431,8 +1412,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atMaximum";testValueName="overLimit";async=true;wgslType="mat3x4%3Cf32%3E"] expected: @@ -1445,8 +1425,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atMaximum";testValueName="overLimit";async=true;wgslType="mat4x2%3Cf32%3E"] expected: @@ -1459,8 +1438,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atMaximum";testValueName="overLimit";async=true;wgslType="mat4x3%3Cf32%3E"] expected: @@ -1473,8 +1451,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atMaximum";testValueName="overLimit";async=true;wgslType="mat4x4%3Cf32%3E"] expected: @@ -1494,8 +1471,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atMaximum";testValueName="overLimit";async=true;wgslType="vec2%3Cf32%3E"] expected: @@ -1522,8 +1498,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atMaximum";testValueName="overLimit";async=true;wgslType="vec3%3Cf32%3E"] expected: @@ -1550,8 +1525,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="atMaximum";testValueName="overLimit";async=true;wgslType="vec4%3Cf32%3E"] expected: @@ -2032,7 +2006,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";async=false;wgslType="f32"] expected: @@ -2054,7 +2028,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";async=false;wgslType="mat2x2%3Cf32%3E"] expected: @@ -2068,7 +2042,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";async=false;wgslType="mat2x3%3Cf32%3E"] expected: @@ -2082,7 +2056,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";async=false;wgslType="mat2x4%3Cf32%3E"] expected: @@ -2096,7 +2070,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";async=false;wgslType="mat3x2%3Cf32%3E"] expected: @@ -2110,7 +2084,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";async=false;wgslType="mat3x3%3Cf32%3E"] expected: @@ -2124,7 +2098,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";async=false;wgslType="mat3x4%3Cf32%3E"] expected: @@ -2138,7 +2112,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";async=false;wgslType="mat4x2%3Cf32%3E"] expected: @@ -2152,7 +2126,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";async=false;wgslType="mat4x3%3Cf32%3E"] expected: @@ -2166,7 +2140,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";async=false;wgslType="mat4x4%3Cf32%3E"] expected: @@ -2188,7 +2162,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";async=false;wgslType="vec2%3Cf32%3E"] expected: @@ -2218,7 +2192,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";async=false;wgslType="vec3%3Cf32%3E"] expected: @@ -2248,7 +2222,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";async=false;wgslType="vec4%3Cf32%3E"] expected: @@ -2302,7 +2276,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";async=true;wgslType="f32"] expected: @@ -2324,7 +2298,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";async=true;wgslType="mat2x2%3Cf32%3E"] expected: @@ -2338,7 +2312,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";async=true;wgslType="mat2x3%3Cf32%3E"] expected: @@ -2352,7 +2326,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";async=true;wgslType="mat2x4%3Cf32%3E"] expected: @@ -2366,7 +2340,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";async=true;wgslType="mat3x2%3Cf32%3E"] expected: @@ -2380,7 +2354,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";async=true;wgslType="mat3x3%3Cf32%3E"] expected: @@ -2394,7 +2368,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";async=true;wgslType="mat3x4%3Cf32%3E"] expected: @@ -2408,7 +2382,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";async=true;wgslType="mat4x2%3Cf32%3E"] expected: @@ -2422,7 +2396,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";async=true;wgslType="mat4x3%3Cf32%3E"] expected: @@ -2436,7 +2410,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";async=true;wgslType="mat4x4%3Cf32%3E"] expected: @@ -2458,7 +2432,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";async=true;wgslType="vec2%3Cf32%3E"] expected: @@ -2488,7 +2462,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";async=true;wgslType="vec3%3Cf32%3E"] expected: @@ -2518,7 +2492,7 @@ expected: if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac" and not debug: FAIL + if os == "mac": FAIL [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";async=true;wgslType="vec4%3Cf32%3E"] expected: diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxInterStageShaderVariables/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxInterStageShaderVariables/cts.https.html.ini index 13aaceecc0a8..dca565eff7db 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxInterStageShaderVariables/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxInterStageShaderVariables/cts.https.html.ini @@ -2180,768 +2180,896 @@ [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=false;frontFacing=false;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=false;frontFacing=false;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=false;frontFacing=false;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=false;frontFacing=false;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=false;frontFacing=false;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=false;frontFacing=false;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=false;frontFacing=false;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=false;frontFacing=false;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=false;frontFacing=true;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=false;frontFacing=true;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=false;frontFacing=true;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=false;frontFacing=true;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=false;frontFacing=true;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=false;frontFacing=true;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=false;frontFacing=true;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=false;frontFacing=true;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=true;frontFacing=false;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=true;frontFacing=false;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=true;frontFacing=false;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=true;frontFacing=false;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=true;frontFacing=false;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=true;frontFacing=false;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=true;frontFacing=false;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=true;frontFacing=false;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=true;frontFacing=true;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=true;frontFacing=true;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=true;frontFacing=true;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=true;frontFacing=true;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=true;frontFacing=true;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=true;frontFacing=true;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=true;frontFacing=true;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=false;pointList=true;frontFacing=true;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=false;frontFacing=false;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=false;frontFacing=false;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=false;frontFacing=false;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=false;frontFacing=false;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=false;frontFacing=false;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=false;frontFacing=false;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=false;frontFacing=false;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=false;frontFacing=false;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=false;frontFacing=true;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=false;frontFacing=true;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=false;frontFacing=true;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=false;frontFacing=true;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=false;frontFacing=true;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=false;frontFacing=true;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=false;frontFacing=true;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=false;frontFacing=true;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=true;frontFacing=false;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=true;frontFacing=false;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=true;frontFacing=false;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=true;frontFacing=false;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=true;frontFacing=false;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=true;frontFacing=false;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=true;frontFacing=false;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=true;frontFacing=false;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=true;frontFacing=true;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=true;frontFacing=true;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=true;frontFacing=true;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=true;frontFacing=true;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=true;frontFacing=true;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=true;frontFacing=true;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=true;frontFacing=true;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";async=true;pointList=true;frontFacing=true;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=false;frontFacing=false;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=false;frontFacing=false;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=false;frontFacing=false;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=false;frontFacing=false;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=false;frontFacing=false;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=false;frontFacing=false;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=false;frontFacing=false;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=false;frontFacing=false;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=false;frontFacing=true;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=false;frontFacing=true;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=false;frontFacing=true;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=false;frontFacing=true;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=false;frontFacing=true;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=false;frontFacing=true;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=false;frontFacing=true;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=false;frontFacing=true;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=true;frontFacing=false;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=true;frontFacing=false;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=true;frontFacing=false;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=true;frontFacing=false;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=true;frontFacing=false;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=true;frontFacing=false;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=true;frontFacing=false;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=true;frontFacing=false;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=true;frontFacing=true;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=true;frontFacing=true;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=true;frontFacing=true;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=true;frontFacing=true;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=true;frontFacing=true;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=true;frontFacing=true;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=true;frontFacing=true;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=false;pointList=true;frontFacing=true;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=false;frontFacing=false;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=false;frontFacing=false;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=false;frontFacing=false;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=false;frontFacing=false;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=false;frontFacing=false;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=false;frontFacing=false;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=false;frontFacing=false;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=false;frontFacing=false;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=false;frontFacing=true;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=false;frontFacing=true;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=false;frontFacing=true;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=false;frontFacing=true;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=false;frontFacing=true;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=false;frontFacing=true;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=false;frontFacing=true;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=false;frontFacing=true;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=true;frontFacing=false;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=true;frontFacing=false;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=true;frontFacing=false;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=true;frontFacing=false;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=true;frontFacing=false;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=true;frontFacing=false;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=true;frontFacing=false;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=true;frontFacing=false;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=true;frontFacing=true;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=true;frontFacing=true;sampleIndex=false;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=true;frontFacing=true;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=true;frontFacing=true;sampleIndex=false;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=true;frontFacing=true;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=true;frontFacing=true;sampleIndex=true;sampleMaskIn=false;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=true;frontFacing=true;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=false] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";async=true;pointList=true;frontFacing=true;sampleIndex=true;sampleMaskIn=true;sampleMaskOut=true] expected: if os == "win" and debug: [PASS, FAIL, TIMEOUT, NOTRUN] + if os == "win" and not debug: FAIL if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxSamplersPerShaderStage/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxSamplersPerShaderStage/cts.https.html.ini index d2e94a3c0917..e1b9d5445f1a 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxSamplersPerShaderStage/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxSamplersPerShaderStage/cts.https.html.ini @@ -1373,244 +1373,604 @@ expected: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="compute";order="backward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="compute";order="backward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="compute";order="forward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="compute";order="forward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="compute";order="shiftByHalf";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="compute";order="shiftByHalf";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="fragment";order="backward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="fragment";order="backward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="fragment";order="forward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="fragment";order="forward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="fragment";order="shiftByHalf";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="fragment";order="shiftByHalf";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="vertex";order="backward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="vertex";order="backward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="vertex";order="forward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="vertex";order="forward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="vertex";order="shiftByHalf";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="vertex";order="shiftByHalf";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow";order="backward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow";order="backward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow";order="forward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow";order="forward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow";order="shiftByHalf";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow";order="shiftByHalf";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow";order="backward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow";order="backward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow";order="forward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow";order="forward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow";order="shiftByHalf";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow";order="shiftByHalf";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="compute";order="backward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="compute";order="backward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="compute";order="forward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="compute";order="forward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="compute";order="shiftByHalf";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="compute";order="shiftByHalf";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="fragment";order="backward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="fragment";order="backward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="fragment";order="forward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="fragment";order="forward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="fragment";order="shiftByHalf";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="fragment";order="shiftByHalf";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="vertex";order="backward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="vertex";order="backward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="vertex";order="forward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="vertex";order="forward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="vertex";order="shiftByHalf";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="vertex";order="shiftByHalf";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow";order="backward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow";order="backward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow";order="forward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow";order="forward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow";order="shiftByHalf";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow";order="shiftByHalf";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow";order="backward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow";order="backward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow";order="forward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow";order="forward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow";order="shiftByHalf";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow";order="shiftByHalf";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="compute";order="backward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="compute";order="backward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="compute";order="forward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="compute";order="forward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="compute";order="shiftByHalf";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="compute";order="shiftByHalf";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="fragment";order="backward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="fragment";order="backward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="fragment";order="forward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="fragment";order="forward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="fragment";order="shiftByHalf";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="fragment";order="shiftByHalf";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="vertex";order="backward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="vertex";order="backward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="vertex";order="forward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="vertex";order="forward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="vertex";order="shiftByHalf";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="vertex";order="shiftByHalf";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow";order="backward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow";order="backward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow";order="forward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow";order="forward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow";order="shiftByHalf";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow";order="shiftByHalf";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow";order="backward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow";order="backward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow";order="forward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow";order="forward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow";order="shiftByHalf";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow";order="shiftByHalf";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="compute";order="backward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="compute";order="backward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="compute";order="forward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="compute";order="forward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="compute";order="shiftByHalf";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="compute";order="shiftByHalf";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="fragment";order="backward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="fragment";order="backward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="fragment";order="forward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="fragment";order="forward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="fragment";order="shiftByHalf";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="fragment";order="shiftByHalf";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="vertex";order="backward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="vertex";order="backward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="vertex";order="forward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="vertex";order="forward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="vertex";order="shiftByHalf";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="vertex";order="shiftByHalf";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow";order="backward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow";order="backward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow";order="forward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow";order="forward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow";order="shiftByHalf";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow";order="shiftByHalf";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow";order="backward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow";order="backward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow";order="forward";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow";order="forward";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow";order="shiftByHalf";bindGroupTest="differentGroups"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow";order="shiftByHalf";bindGroupTest="sameGroup"] + expected: + if os == "win" and debug: [PASS, FAIL] + if os == "win" and not debug: FAIL [:limitTest="underDefault";testValueName="atLimit";async=false;bindingCombination="compute";order="backward";bindGroupTest="differentGroups"] diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxStorageBuffersPerShaderStage/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxStorageBuffersPerShaderStage/cts.https.html.ini index 1ac6e4dd2241..e310a06df437 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxStorageBuffersPerShaderStage/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxStorageBuffersPerShaderStage/cts.https.html.ini @@ -608,84 +608,44 @@ implementation-status: backlog expected: [OK, CRASH] [:limitTest="atDefault";testValueName="atLimit";async=false;bindingCombination="compute"] - expected: - if os == "win" and not debug: FAIL [:limitTest="atDefault";testValueName="atLimit";async=false;bindingCombination="fragment"] - expected: - if os == "win" and not debug: FAIL [:limitTest="atDefault";testValueName="atLimit";async=false;bindingCombination="vertex"] - expected: - if os == "win" and not debug: FAIL [:limitTest="atDefault";testValueName="atLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow"] - expected: - if os == "win" and not debug: FAIL [:limitTest="atDefault";testValueName="atLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow"] - expected: - if os == "win" and not debug: FAIL [:limitTest="atDefault";testValueName="atLimit";async=true;bindingCombination="compute"] - expected: - if os == "win" and not debug: FAIL [:limitTest="atDefault";testValueName="atLimit";async=true;bindingCombination="fragment"] - expected: - if os == "win" and not debug: FAIL [:limitTest="atDefault";testValueName="atLimit";async=true;bindingCombination="vertex"] - expected: - if os == "win" and not debug: FAIL [:limitTest="atDefault";testValueName="atLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow"] - expected: - if os == "win" and not debug: FAIL [:limitTest="atDefault";testValueName="atLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow"] - expected: - if os == "win" and not debug: FAIL [:limitTest="atDefault";testValueName="overLimit";async=false;bindingCombination="compute"] - expected: - if os == "win" and not debug: FAIL [:limitTest="atDefault";testValueName="overLimit";async=false;bindingCombination="fragment"] - expected: - if os == "win" and not debug: FAIL [:limitTest="atDefault";testValueName="overLimit";async=false;bindingCombination="vertex"] - expected: - if os == "win" and not debug: FAIL [:limitTest="atDefault";testValueName="overLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow"] - expected: - if os == "win" and not debug: FAIL [:limitTest="atDefault";testValueName="overLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow"] - expected: - if os == "win" and not debug: FAIL [:limitTest="atDefault";testValueName="overLimit";async=true;bindingCombination="compute"] - expected: - if os == "win" and not debug: FAIL [:limitTest="atDefault";testValueName="overLimit";async=true;bindingCombination="fragment"] - expected: - if os == "win" and not debug: FAIL [:limitTest="atDefault";testValueName="overLimit";async=true;bindingCombination="vertex"] - expected: - if os == "win" and not debug: FAIL [:limitTest="atDefault";testValueName="overLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow"] - expected: - if os == "win" and not debug: FAIL [:limitTest="atDefault";testValueName="overLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow"] - expected: - if os == "win" and not debug: FAIL [:limitTest="atMaximum";testValueName="atLimit";async=false;bindingCombination="compute"] expected: @@ -840,95 +800,49 @@ [:limitTest="overMaximum";testValueName="overLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow"] [:limitTest="underDefault";testValueName="atLimit";async=false;bindingCombination="compute"] - expected: - if os == "win" and not debug: FAIL [:limitTest="underDefault";testValueName="atLimit";async=false;bindingCombination="fragment"] - expected: - if os == "win" and not debug: FAIL [:limitTest="underDefault";testValueName="atLimit";async=false;bindingCombination="vertex"] - expected: - if os == "win" and not debug: FAIL [:limitTest="underDefault";testValueName="atLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow"] - expected: - if os == "win" and not debug: FAIL [:limitTest="underDefault";testValueName="atLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow"] - expected: - if os == "win" and not debug: FAIL [:limitTest="underDefault";testValueName="atLimit";async=true;bindingCombination="compute"] - expected: - if os == "win" and not debug: FAIL [:limitTest="underDefault";testValueName="atLimit";async=true;bindingCombination="fragment"] - expected: - if os == "win" and not debug: FAIL [:limitTest="underDefault";testValueName="atLimit";async=true;bindingCombination="vertex"] - expected: - if os == "win" and not debug: FAIL [:limitTest="underDefault";testValueName="atLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow"] - expected: - if os == "win" and not debug: FAIL [:limitTest="underDefault";testValueName="atLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow"] - expected: - if os == "win" and not debug: FAIL [:limitTest="underDefault";testValueName="overLimit";async=false;bindingCombination="compute"] - expected: - if os == "win" and not debug: FAIL [:limitTest="underDefault";testValueName="overLimit";async=false;bindingCombination="fragment"] - expected: - if os == "win" and not debug: FAIL [:limitTest="underDefault";testValueName="overLimit";async=false;bindingCombination="vertex"] - expected: - if os == "win" and not debug: FAIL [:limitTest="underDefault";testValueName="overLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow"] - expected: - if os == "win" and not debug: FAIL [:limitTest="underDefault";testValueName="overLimit";async=false;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow"] - expected: - if os == "win" and not debug: FAIL [:limitTest="underDefault";testValueName="overLimit";async=true;bindingCombination="compute"] - expected: - if os == "win" and not debug: FAIL [:limitTest="underDefault";testValueName="overLimit";async=true;bindingCombination="fragment"] - expected: - if os == "win" and not debug: FAIL [:limitTest="underDefault";testValueName="overLimit";async=true;bindingCombination="vertex"] - expected: - if os == "win" and not debug: FAIL [:limitTest="underDefault";testValueName="overLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow"] - expected: - if os == "win" and not debug: FAIL [:limitTest="underDefault";testValueName="overLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleVertexStageOverflow"] - expected: - if os == "win" and not debug: FAIL [cts.https.html?q=webgpu:api,validation,capability_checks,limits,maxStorageBuffersPerShaderStage:createPipelineLayout,at_over:*] - implementation-status: - if os == "win" and debug: backlog - if os == "linux": backlog - if os == "mac": backlog - expected: - if os == "win" and debug: [OK, TIMEOUT] - if os == "linux": [OK, TIMEOUT] - if os == "mac": [OK, TIMEOUT] + implementation-status: backlog + expected: [OK, TIMEOUT] [:limitTest="atDefault";testValueName="atLimit";visibility=1;type="read-only-storage";order="backward"] [:limitTest="atDefault";testValueName="atLimit";visibility=1;type="read-only-storage";order="forward"] @@ -1071,21 +985,25 @@ [:limitTest="atDefault";testValueName="overLimit";visibility=2;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="overLimit";visibility=2;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="overLimit";visibility=2;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="overLimit";visibility=2;type="storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] @@ -1096,1430 +1014,1302 @@ [:limitTest="atDefault";testValueName="overLimit";visibility=2;type="storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="overLimit";visibility=3;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="overLimit";visibility=3;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="overLimit";visibility=3;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="overLimit";visibility=4;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="overLimit";visibility=4;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="overLimit";visibility=4;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="overLimit";visibility=4;type="storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="overLimit";visibility=4;type="storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="overLimit";visibility=4;type="storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="overLimit";visibility=5;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="overLimit";visibility=5;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="overLimit";visibility=5;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="overLimit";visibility=6;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="overLimit";visibility=6;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="overLimit";visibility=6;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="overLimit";visibility=6;type="storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="overLimit";visibility=6;type="storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="overLimit";visibility=6;type="storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="overLimit";visibility=7;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="overLimit";visibility=7;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="overLimit";visibility=7;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=1;type="read-only-storage";order="backward"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=1;type="read-only-storage";order="forward"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=1;type="read-only-storage";order="shiftByHalf"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=2;type="read-only-storage";order="backward"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=2;type="read-only-storage";order="forward"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=2;type="read-only-storage";order="shiftByHalf"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=2;type="storage";order="backward"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=2;type="storage";order="forward"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=2;type="storage";order="shiftByHalf"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=3;type="read-only-storage";order="backward"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=3;type="read-only-storage";order="forward"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=3;type="read-only-storage";order="shiftByHalf"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=4;type="read-only-storage";order="backward"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=4;type="read-only-storage";order="forward"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=4;type="read-only-storage";order="shiftByHalf"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=4;type="storage";order="backward"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=4;type="storage";order="forward"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=4;type="storage";order="shiftByHalf"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=5;type="read-only-storage";order="backward"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=5;type="read-only-storage";order="forward"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=5;type="read-only-storage";order="shiftByHalf"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=6;type="read-only-storage";order="backward"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=6;type="read-only-storage";order="forward"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=6;type="read-only-storage";order="shiftByHalf"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=6;type="storage";order="backward"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=6;type="storage";order="forward"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=6;type="storage";order="shiftByHalf"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=7;type="read-only-storage";order="backward"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=7;type="read-only-storage";order="forward"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="atLimit";visibility=7;type="read-only-storage";order="shiftByHalf"] expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] + if os == "win": [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [FAIL, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=1;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=1;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=1;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=2;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=2;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=2;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=2;type="storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=2;type="storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=2;type="storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=3;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=3;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=3;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=4;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=4;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=4;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=4;type="storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=4;type="storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=4;type="storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=5;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=5;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=5;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=6;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=6;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=6;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=6;type="storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=6;type="storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=6;type="storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=7;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=7;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="atMaximum";testValueName="overLimit";visibility=7;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=1;type="read-only-storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=1;type="read-only-storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=1;type="read-only-storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=2;type="read-only-storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=2;type="read-only-storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=2;type="read-only-storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=2;type="storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=2;type="storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=2;type="storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=3;type="read-only-storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=3;type="read-only-storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=3;type="read-only-storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=4;type="read-only-storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=4;type="read-only-storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=4;type="read-only-storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=4;type="storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=4;type="storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=4;type="storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=5;type="read-only-storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=5;type="read-only-storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=5;type="read-only-storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=6;type="read-only-storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=6;type="read-only-storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=6;type="read-only-storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=6;type="storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=6;type="storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=6;type="storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=7;type="read-only-storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=7;type="read-only-storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";visibility=7;type="read-only-storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=1;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=1;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=1;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=2;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=2;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=2;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=2;type="storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=2;type="storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=2;type="storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=3;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=3;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=3;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=4;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=4;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=4;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=4;type="storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=4;type="storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=4;type="storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=5;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=5;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=5;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=6;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=6;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=6;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=6;type="storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=6;type="storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=6;type="storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=7;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=7;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";visibility=7;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=1;type="read-only-storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=1;type="read-only-storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=1;type="read-only-storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=2;type="read-only-storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=2;type="read-only-storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=2;type="read-only-storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=2;type="storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=2;type="storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=2;type="storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=3;type="read-only-storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=3;type="read-only-storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=3;type="read-only-storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=4;type="read-only-storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=4;type="read-only-storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=4;type="read-only-storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=4;type="storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=4;type="storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=4;type="storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=5;type="read-only-storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=5;type="read-only-storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=5;type="read-only-storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=6;type="read-only-storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=6;type="read-only-storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=6;type="read-only-storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=6;type="storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=6;type="storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=6;type="storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=7;type="read-only-storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=7;type="read-only-storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="atLimit";visibility=7;type="read-only-storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=1;type="read-only-storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=1;type="read-only-storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=1;type="read-only-storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=2;type="read-only-storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=2;type="read-only-storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=2;type="read-only-storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=2;type="storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=2;type="storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=2;type="storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=3;type="read-only-storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=3;type="read-only-storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=3;type="read-only-storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=4;type="read-only-storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=4;type="read-only-storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=4;type="read-only-storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=4;type="storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=4;type="storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=4;type="storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=5;type="read-only-storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=5;type="read-only-storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=5;type="read-only-storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=6;type="read-only-storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=6;type="read-only-storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=6;type="read-only-storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=6;type="storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=6;type="storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=6;type="storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=7;type="read-only-storage";order="backward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=7;type="read-only-storage";order="forward"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="overMaximum";testValueName="overLimit";visibility=7;type="read-only-storage";order="shiftByHalf"] - expected: - if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux": [PASS, TIMEOUT, NOTRUN] - if os == "mac": [PASS, TIMEOUT, NOTRUN] + expected: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=1;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=1;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=1;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=2;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=2;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=2;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=2;type="storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=2;type="storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=2;type="storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=3;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=3;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=3;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=4;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=4;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=4;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=4;type="storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=4;type="storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=4;type="storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=5;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=5;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=5;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=6;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=6;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=6;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=6;type="storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=6;type="storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=6;type="storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=7;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=7;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="atLimit";visibility=7;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=1;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=1;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=1;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=2;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=2;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=2;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=2;type="storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=2;type="storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=2;type="storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=3;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=3;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=3;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=4;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=4;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=4;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=4;type="storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=4;type="storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=4;type="storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=5;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=5;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=5;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=6;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=6;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=6;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=6;type="storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=6;type="storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=6;type="storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=7;type="read-only-storage";order="backward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=7;type="read-only-storage";order="forward"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="underDefault";testValueName="overLimit";visibility=7;type="read-only-storage";order="shiftByHalf"] expected: + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxStorageTexturesPerShaderStage/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxStorageTexturesPerShaderStage/cts.https.html.ini index 129d4f004528..0c8b23cb2e50 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxStorageTexturesPerShaderStage/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxStorageTexturesPerShaderStage/cts.https.html.ini @@ -797,6 +797,8 @@ [:limitTest="atDefault";testValueName="atLimit";async=false;bindingCombination="compute";access="read-write"] [:limitTest="atDefault";testValueName="atLimit";async=false;bindingCombination="compute";access="write-only"] + expected: + if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="atLimit";async=false;bindingCombination="fragment";access="read-only"] @@ -823,14 +825,20 @@ if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="atLimit";async=true;bindingCombination="fragment";access="read-only"] + expected: + if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="atLimit";async=true;bindingCombination="fragment";access="read-write"] + expected: + if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="atLimit";async=true;bindingCombination="fragment";access="write-only"] expected: if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="atLimit";async=true;bindingCombination="vertex";access="read-only"] + expected: + if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] [:limitTest="atDefault";testValueName="atLimit";async=true;bindingCombination="vertexAndFragmentWithPossibleFragmentStageOverflow";access="read-only"] expected: diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxTextureDimension2D/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxTextureDimension2D/cts.https.html.ini index 52afc284a499..d6b480a4f2b0 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxTextureDimension2D/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxTextureDimension2D/cts.https.html.ini @@ -1,24 +1,16 @@ [cts.https.html?q=webgpu:api,validation,capability_checks,limits,maxTextureDimension2D:configure,at_over:*] tags: [webgpu, webgpu-long] implementation-status: - if os == "mac": backlog + if os == "mac" and not debug: backlog expected: if os == "mac" and not debug: [OK, CRASH] [:limitTest="atDefault";testValueName="atLimit";canvasType="offscreen"] - expected: - if os == "mac" and debug: FAIL [:limitTest="atDefault";testValueName="atLimit";canvasType="onscreen"] - expected: - if os == "mac" and debug: FAIL [:limitTest="atDefault";testValueName="overLimit";canvasType="offscreen"] - expected: - if os == "mac" and debug: FAIL [:limitTest="atDefault";testValueName="overLimit";canvasType="onscreen"] - expected: - if os == "mac" and debug: FAIL [:limitTest="atMaximum";testValueName="atLimit";canvasType="offscreen"] @@ -45,20 +37,12 @@ [:limitTest="overMaximum";testValueName="overLimit";canvasType="onscreen"] [:limitTest="underDefault";testValueName="atLimit";canvasType="offscreen"] - expected: - if os == "mac" and debug: FAIL [:limitTest="underDefault";testValueName="atLimit";canvasType="onscreen"] - expected: - if os == "mac" and debug: FAIL [:limitTest="underDefault";testValueName="overLimit";canvasType="offscreen"] - expected: - if os == "mac" and debug: FAIL [:limitTest="underDefault";testValueName="overLimit";canvasType="onscreen"] - expected: - if os == "mac" and debug: FAIL [cts.https.html?q=webgpu:api,validation,capability_checks,limits,maxTextureDimension2D:createTexture,at_over:*] @@ -87,24 +71,15 @@ tags: [webgpu, webgpu-long] implementation-status: if os == "linux" and not debug: backlog - if os == "mac" and debug: backlog expected: if os == "linux" and not debug: [OK, CRASH] [:limitTest="atDefault";testValueName="atLimit";canvasType="offscreen"] - expected: - if os == "mac" and debug: FAIL [:limitTest="atDefault";testValueName="atLimit";canvasType="onscreen"] - expected: - if os == "mac" and debug: FAIL [:limitTest="atDefault";testValueName="overLimit";canvasType="offscreen"] - expected: - if os == "mac" and debug: FAIL [:limitTest="atDefault";testValueName="overLimit";canvasType="onscreen"] - expected: - if os == "mac" and debug: FAIL [:limitTest="atMaximum";testValueName="atLimit";canvasType="offscreen"] @@ -131,17 +106,9 @@ [:limitTest="overMaximum";testValueName="overLimit";canvasType="onscreen"] [:limitTest="underDefault";testValueName="atLimit";canvasType="offscreen"] - expected: - if os == "mac" and debug: FAIL [:limitTest="underDefault";testValueName="atLimit";canvasType="onscreen"] - expected: - if os == "mac" and debug: FAIL [:limitTest="underDefault";testValueName="overLimit";canvasType="offscreen"] - expected: - if os == "mac" and debug: FAIL [:limitTest="underDefault";testValueName="overLimit";canvasType="onscreen"] - expected: - if os == "mac" and debug: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxUniformBufferBindingSize/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxUniformBufferBindingSize/cts.https.html.ini index e5bd62e57e16..05eb21054b02 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxUniformBufferBindingSize/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/capability_checks/limits/maxUniformBufferBindingSize/cts.https.html.ini @@ -1,22 +1,12 @@ [cts.https.html?q=webgpu:api,validation,capability_checks,limits,maxUniformBufferBindingSize:createBindGroup,at_over:*] tags: [webgpu, webgpu-long] - implementation-status: - if os == "win" and debug: backlog [:limitTest="atDefault";testValueName="atLimit";bufferPart="biggerBufferWithOffset"] - expected: - if os == "win" and debug: FAIL [:limitTest="atDefault";testValueName="atLimit";bufferPart="wholeBuffer"] - expected: - if os == "win" and debug: FAIL [:limitTest="atDefault";testValueName="overLimit";bufferPart="biggerBufferWithOffset"] - expected: - if os == "win" and debug: FAIL [:limitTest="atDefault";testValueName="overLimit";bufferPart="wholeBuffer"] - expected: - if os == "win" and debug: FAIL [:limitTest="atMaximum";testValueName="atLimit";bufferPart="biggerBufferWithOffset"] @@ -27,20 +17,12 @@ [:limitTest="atMaximum";testValueName="overLimit";bufferPart="wholeBuffer"] [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";bufferPart="biggerBufferWithOffset"] - expected: - if os == "win" and debug: FAIL [:limitTest="betweenDefaultAndMaximum";testValueName="atLimit";bufferPart="wholeBuffer"] - expected: - if os == "win" and debug: FAIL [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";bufferPart="biggerBufferWithOffset"] - expected: - if os == "win" and debug: FAIL [:limitTest="betweenDefaultAndMaximum";testValueName="overLimit";bufferPart="wholeBuffer"] - expected: - if os == "win" and debug: FAIL [:limitTest="overMaximum";testValueName="atLimit";bufferPart="biggerBufferWithOffset"] @@ -51,20 +33,12 @@ [:limitTest="overMaximum";testValueName="overLimit";bufferPart="wholeBuffer"] [:limitTest="underDefault";testValueName="atLimit";bufferPart="biggerBufferWithOffset"] - expected: - if os == "win" and debug: FAIL [:limitTest="underDefault";testValueName="atLimit";bufferPart="wholeBuffer"] - expected: - if os == "win" and debug: FAIL [:limitTest="underDefault";testValueName="overLimit";bufferPart="biggerBufferWithOffset"] - expected: - if os == "win" and debug: FAIL [:limitTest="underDefault";testValueName="overLimit";bufferPart="wholeBuffer"] - expected: - if os == "win" and debug: FAIL [cts.https.html?q=webgpu:api,validation,capability_checks,limits,maxUniformBufferBindingSize:validate,maxBufferSize:*] diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/compute_pipeline/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/compute_pipeline/cts.https.html.ini index 9f77934498e3..63a27d569222 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/compute_pipeline/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/compute_pipeline/cts.https.html.ini @@ -208,6 +208,8 @@ [cts.https.html?q=webgpu:api,validation,compute_pipeline:overrides,value,validation_error,f16:*] implementation-status: if os == "mac": backlog + expected: + if os == "mac": CRASH [:isAsync=false;constants={"cf16":-3.4028234663852886e%2B38}] expected: if os == "mac": FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/error_scope/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/error_scope/cts.https.html.ini index ed34713e4017..31c9713ead2d 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/error_scope/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/error_scope/cts.https.html.ini @@ -123,8 +123,6 @@ implementation-status: if os == "win": backlog if os == "mac": backlog - expected: - if os == "win" and debug: SKIP [:errorFilter="out-of-memory";stackDepth=1] expected: if os == "win": FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/render_pass/render_pass_descriptor/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/render_pass/render_pass_descriptor/cts.https.html.ini index 65971b581165..4cccded5ecd5 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/render_pass/render_pass_descriptor/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/render_pass/render_pass_descriptor/cts.https.html.ini @@ -642,10 +642,7 @@ if os == "mac": [PASS, TIMEOUT, NOTRUN] [:format="rg11b10ufloat"] - expected: - if os == "win" and not debug: FAIL - if os == "linux": FAIL - if os == "mac": FAIL + expected: FAIL [:format="rg16float"] expected: diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/render_pipeline/overrides/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/render_pipeline/overrides/cts.https.html.ini index 913d1759ac08..3e2f6b7bd6dd 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/render_pipeline/overrides/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/render_pipeline/overrides/cts.https.html.ini @@ -209,6 +209,8 @@ [cts.https.html?q=webgpu:api,validation,render_pipeline,overrides:value,validation_error,f16,fragment:*] implementation-status: if os == "mac": backlog + expected: + if os == "mac": CRASH [:isAsync=false;fragmentConstants={"cf16":-3.4028234663852886e%2B38}] expected: if os == "mac": FAIL @@ -277,6 +279,8 @@ [cts.https.html?q=webgpu:api,validation,render_pipeline,overrides:value,validation_error,f16,vertex:*] implementation-status: if os == "mac": backlog + expected: + if os == "mac": CRASH [:isAsync=false;vertexConstants={"cf16":-3.4028234663852886e%2B38}] expected: if os == "mac": FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/state/device_lost/destroy/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/state/device_lost/destroy/cts.https.html.ini index 6d50ba702899..79db1321e4d5 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/state/device_lost/destroy/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/api/validation/state/device_lost/destroy/cts.https.html.ini @@ -1225,6 +1225,8 @@ [:format="astc-6x5-unorm";usageType="texture";usageCopy="none";awaitLost=true] [:format="astc-6x5-unorm";usageType="texture";usageCopy="src";awaitLost=false] + expected: + if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] [:format="astc-6x5-unorm";usageType="texture";usageCopy="src";awaitLost=true] @@ -1310,23 +1312,19 @@ [:format="astc-6x6-unorm-srgb";usageType="texture";usageCopy="none";awaitLost=false] expected: - if os == "mac" and debug: [TIMEOUT, NOTRUN] - if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "mac": [PASS, TIMEOUT, NOTRUN] [:format="astc-6x6-unorm-srgb";usageType="texture";usageCopy="none";awaitLost=true] expected: - if os == "mac" and debug: [TIMEOUT, NOTRUN] - if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "mac": [PASS, TIMEOUT, NOTRUN] [:format="astc-6x6-unorm-srgb";usageType="texture";usageCopy="src";awaitLost=false] expected: - if os == "mac" and debug: [TIMEOUT, NOTRUN] - if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "mac": [PASS, TIMEOUT, NOTRUN] [:format="astc-6x6-unorm-srgb";usageType="texture";usageCopy="src";awaitLost=true] expected: - if os == "mac" and debug: [TIMEOUT, NOTRUN] - if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "mac": [PASS, TIMEOUT, NOTRUN] [:format="astc-6x6-unorm-srgb";usageType="texture";usageCopy="src-dest";awaitLost=false] expected: @@ -1370,13 +1368,11 @@ [:format="astc-8x5-unorm-srgb";usageType="texture";usageCopy="dst";awaitLost=false] expected: - if os == "mac" and debug: [TIMEOUT, NOTRUN] - if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "mac": [PASS, TIMEOUT, NOTRUN] [:format="astc-8x5-unorm-srgb";usageType="texture";usageCopy="dst";awaitLost=true] expected: - if os == "mac" and debug: [TIMEOUT, NOTRUN] - if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "mac": [PASS, TIMEOUT, NOTRUN] [:format="astc-8x5-unorm-srgb";usageType="texture";usageCopy="none";awaitLost=false] expected: @@ -1388,8 +1384,7 @@ [:format="astc-8x5-unorm-srgb";usageType="texture";usageCopy="src";awaitLost=false] expected: - if os == "mac" and debug: [TIMEOUT, NOTRUN] - if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "mac": [PASS, TIMEOUT, NOTRUN] [:format="astc-8x5-unorm-srgb";usageType="texture";usageCopy="src";awaitLost=true] expected: @@ -1397,13 +1392,11 @@ [:format="astc-8x5-unorm-srgb";usageType="texture";usageCopy="src-dest";awaitLost=false] expected: - if os == "mac" and debug: [TIMEOUT, NOTRUN] - if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "mac": [PASS, TIMEOUT, NOTRUN] [:format="astc-8x5-unorm-srgb";usageType="texture";usageCopy="src-dest";awaitLost=true] expected: - if os == "mac" and debug: [TIMEOUT, NOTRUN] - if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "mac": [PASS, TIMEOUT, NOTRUN] [:format="astc-8x6-unorm";usageType="texture";usageCopy="dst";awaitLost=false] expected: @@ -1417,23 +1410,19 @@ [:format="astc-8x6-unorm";usageType="texture";usageCopy="none";awaitLost=false] expected: - if os == "mac" and debug: [TIMEOUT, NOTRUN] - if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "mac": [PASS, TIMEOUT, NOTRUN] [:format="astc-8x6-unorm";usageType="texture";usageCopy="none";awaitLost=true] expected: - if os == "mac" and debug: [TIMEOUT, NOTRUN] - if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "mac": [PASS, TIMEOUT, NOTRUN] [:format="astc-8x6-unorm";usageType="texture";usageCopy="src";awaitLost=false] expected: - if os == "mac" and debug: [TIMEOUT, NOTRUN] - if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "mac": [PASS, TIMEOUT, NOTRUN] [:format="astc-8x6-unorm";usageType="texture";usageCopy="src";awaitLost=true] expected: - if os == "mac" and debug: [TIMEOUT, NOTRUN] - if os == "mac" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "mac": [PASS, TIMEOUT, NOTRUN] [:format="astc-8x6-unorm";usageType="texture";usageCopy="src-dest";awaitLost=false] expected: diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/binary/af_matrix_matrix_multiplication/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/binary/af_matrix_matrix_multiplication/cts.https.html.ini index c5e0fd59a9ff..ec4d0ea64a42 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/binary/af_matrix_matrix_multiplication/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/binary/af_matrix_matrix_multiplication/cts.https.html.ini @@ -1,82 +1,192 @@ [cts.https.html?q=webgpu:shader,execution,expression,binary,af_matrix_matrix_multiplication:matrix_matrix:*] implementation-status: backlog + expected: + if os == "win" and not debug: [OK, TIMEOUT] [:inputSource="const";common_dim=2;x_rows=2;y_cols=2] - expected: FAIL + expected: + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] + if os == "linux": FAIL + if os == "mac": FAIL [:inputSource="const";common_dim=2;x_rows=2;y_cols=3] - expected: FAIL + expected: + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] + if os == "linux": FAIL + if os == "mac": FAIL [:inputSource="const";common_dim=2;x_rows=2;y_cols=4] - expected: FAIL + expected: + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] + if os == "linux": FAIL + if os == "mac": FAIL [:inputSource="const";common_dim=2;x_rows=3;y_cols=2] - expected: FAIL + expected: + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] + if os == "linux": FAIL + if os == "mac": FAIL [:inputSource="const";common_dim=2;x_rows=3;y_cols=3] - expected: FAIL + expected: + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] + if os == "linux": FAIL + if os == "mac": FAIL [:inputSource="const";common_dim=2;x_rows=3;y_cols=4] - expected: FAIL + expected: + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] + if os == "linux": FAIL + if os == "mac": FAIL [:inputSource="const";common_dim=2;x_rows=4;y_cols=2] - expected: FAIL + expected: + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] + if os == "linux": FAIL + if os == "mac": FAIL [:inputSource="const";common_dim=2;x_rows=4;y_cols=3] - expected: FAIL + expected: + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] + if os == "linux": FAIL + if os == "mac": FAIL [:inputSource="const";common_dim=2;x_rows=4;y_cols=4] - expected: FAIL + expected: + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] + if os == "linux": FAIL + if os == "mac": FAIL [:inputSource="const";common_dim=3;x_rows=2;y_cols=2] - expected: FAIL + expected: + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] + if os == "linux": FAIL + if os == "mac": FAIL [:inputSource="const";common_dim=3;x_rows=2;y_cols=3] - expected: FAIL + expected: + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] + if os == "linux": FAIL + if os == "mac": FAIL [:inputSource="const";common_dim=3;x_rows=2;y_cols=4] - expected: FAIL + expected: + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] + if os == "linux": FAIL + if os == "mac": FAIL [:inputSource="const";common_dim=3;x_rows=3;y_cols=2] - expected: FAIL + expected: + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] + if os == "linux": FAIL + if os == "mac": FAIL [:inputSource="const";common_dim=3;x_rows=3;y_cols=3] - expected: FAIL + expected: + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] + if os == "linux": FAIL + if os == "mac": FAIL [:inputSource="const";common_dim=3;x_rows=3;y_cols=4] - expected: FAIL + expected: + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] + if os == "linux": FAIL + if os == "mac": FAIL [:inputSource="const";common_dim=3;x_rows=4;y_cols=2] - expected: FAIL + expected: + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] + if os == "linux": FAIL + if os == "mac": FAIL [:inputSource="const";common_dim=3;x_rows=4;y_cols=3] - expected: FAIL + expected: + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] + if os == "linux": FAIL + if os == "mac": FAIL [:inputSource="const";common_dim=3;x_rows=4;y_cols=4] - expected: FAIL + expected: + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] + if os == "linux": FAIL + if os == "mac": FAIL [:inputSource="const";common_dim=4;x_rows=2;y_cols=2] - expected: FAIL + expected: + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] + if os == "linux": FAIL + if os == "mac": FAIL [:inputSource="const";common_dim=4;x_rows=2;y_cols=3] - expected: FAIL + expected: + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] + if os == "linux": FAIL + if os == "mac": FAIL [:inputSource="const";common_dim=4;x_rows=2;y_cols=4] - expected: FAIL + expected: + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] + if os == "linux": FAIL + if os == "mac": FAIL [:inputSource="const";common_dim=4;x_rows=3;y_cols=2] - expected: FAIL + expected: + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] + if os == "linux": FAIL + if os == "mac": FAIL [:inputSource="const";common_dim=4;x_rows=3;y_cols=3] - expected: FAIL + expected: + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] + if os == "linux": FAIL + if os == "mac": FAIL [:inputSource="const";common_dim=4;x_rows=3;y_cols=4] - expected: FAIL + expected: + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] + if os == "linux": FAIL + if os == "mac": FAIL [:inputSource="const";common_dim=4;x_rows=4;y_cols=2] - expected: FAIL + expected: + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] + if os == "linux": FAIL + if os == "mac": FAIL [:inputSource="const";common_dim=4;x_rows=4;y_cols=3] - expected: FAIL + expected: + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] + if os == "linux": FAIL + if os == "mac": FAIL [:inputSource="const";common_dim=4;x_rows=4;y_cols=4] - expected: FAIL + expected: + if os == "win" and debug: FAIL + if os == "win" and not debug: [FAIL, TIMEOUT, NOTRUN] + if os == "linux": FAIL + if os == "mac": FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/call/builtin/cross/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/call/builtin/cross/cts.https.html.ini index bcbddc2774f8..68674262244f 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/call/builtin/cross/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/call/builtin/cross/cts.https.html.ini @@ -5,11 +5,7 @@ [cts.https.html?q=webgpu:shader,execution,expression,call,builtin,cross:f16:*] - implementation-status: - if os == "mac": backlog [:inputSource="const"] - expected: - if os == "mac": FAIL [:inputSource="storage_r"] @@ -19,9 +15,7 @@ [cts.https.html?q=webgpu:shader,execution,expression,call,builtin,cross:f32:*] - implementation-status: backlog [:inputSource="const"] - expected: FAIL [:inputSource="storage_r"] diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/call/builtin/subgroupMinMax/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/call/builtin/subgroupMinMax/cts.https.html.ini index 9258cfb0f868..d142e85cad8f 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/call/builtin/subgroupMinMax/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/call/builtin/subgroupMinMax/cts.https.html.ini @@ -1544,8 +1544,12 @@ [:case=175;type="f32";op="subgroupMin";wgSize=[64,2,1\]] [:case=176;type="f16";op="subgroupMax";wgSize=[128,1,1\]] + expected: + if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] [:case=176;type="f16";op="subgroupMax";wgSize=[64,2,1\]] + expected: + if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] [:case=176;type="f16";op="subgroupMin";wgSize=[128,1,1\]] @@ -1576,14 +1580,20 @@ if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] [:case=177;type="f32";op="subgroupMax";wgSize=[128,1,1\]] + expected: + if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] [:case=177;type="f32";op="subgroupMax";wgSize=[64,2,1\]] expected: if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] [:case=177;type="f32";op="subgroupMin";wgSize=[128,1,1\]] + expected: + if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] [:case=177;type="f32";op="subgroupMin";wgSize=[64,2,1\]] + expected: + if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] [:case=178;type="f16";op="subgroupMax";wgSize=[128,1,1\]] expected: @@ -7890,41 +7900,35 @@ [:case=343;type="f16";op="subgroupMax";wgSize=[128,1,1\]] expected: if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] [:case=343;type="f16";op="subgroupMax";wgSize=[64,2,1\]] expected: if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] [:case=343;type="f16";op="subgroupMin";wgSize=[128,1,1\]] expected: if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] [:case=343;type="f16";op="subgroupMin";wgSize=[64,2,1\]] expected: if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] [:case=343;type="f32";op="subgroupMax";wgSize=[128,1,1\]] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] [:case=343;type="f32";op="subgroupMax";wgSize=[64,2,1\]] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] [:case=343;type="f32";op="subgroupMin";wgSize=[128,1,1\]] @@ -7934,64 +7938,55 @@ [:case=343;type="f32";op="subgroupMin";wgSize=[64,2,1\]] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] [:case=344;type="f16";op="subgroupMax";wgSize=[128,1,1\]] expected: if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] [:case=344;type="f16";op="subgroupMax";wgSize=[64,2,1\]] expected: if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] [:case=344;type="f16";op="subgroupMin";wgSize=[128,1,1\]] expected: if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] [:case=344;type="f16";op="subgroupMin";wgSize=[64,2,1\]] expected: if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] [:case=344;type="f32";op="subgroupMax";wgSize=[128,1,1\]] expected: if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] [:case=344;type="f32";op="subgroupMax";wgSize=[64,2,1\]] expected: if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] [:case=344;type="f32";op="subgroupMin";wgSize=[128,1,1\]] expected: if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] [:case=344;type="f32";op="subgroupMin";wgSize=[64,2,1\]] expected: if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] [:case=345;type="f16";op="subgroupMax";wgSize=[128,1,1\]] @@ -8039,15 +8034,13 @@ [:case=345;type="f32";op="subgroupMin";wgSize=[128,1,1\]] expected: if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] [:case=345;type="f32";op="subgroupMin";wgSize=[64,2,1\]] expected: if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] [:case=346;type="f16";op="subgroupMax";wgSize=[128,1,1\]] diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/call/builtin/subgroupMul/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/call/builtin/subgroupMul/cts.https.html.ini index 39258c0ddc6d..1a9976aa3f5e 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/call/builtin/subgroupMul/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/call/builtin/subgroupMul/cts.https.html.ini @@ -2336,8 +2336,12 @@ [:case=358;type="f32";wgSize=[64,2,1\]] [:case=359;type="f16";wgSize=[128,1,1\]] + expected: + if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] [:case=359;type="f16";wgSize=[64,2,1\]] + expected: + if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] [:case=359;type="f32";wgSize=[128,1,1\]] @@ -2352,22 +2356,36 @@ [:case=35;type="f32";wgSize=[64,2,1\]] [:case=360;type="f16";wgSize=[128,1,1\]] + expected: + if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] [:case=360;type="f16";wgSize=[64,2,1\]] + expected: + if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] [:case=360;type="f32";wgSize=[128,1,1\]] + expected: + if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] [:case=360;type="f32";wgSize=[64,2,1\]] + expected: + if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] [:case=361;type="f16";wgSize=[128,1,1\]] + expected: + if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] [:case=361;type="f16";wgSize=[64,2,1\]] expected: if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] [:case=361;type="f32";wgSize=[128,1,1\]] + expected: + if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] [:case=361;type="f32";wgSize=[64,2,1\]] + expected: + if os == "mac" and debug: [PASS, TIMEOUT, NOTRUN] [:case=362;type="f16";wgSize=[128,1,1\]] expected: @@ -8847,14 +8865,12 @@ [:case=710;type="f16";wgSize=[128,1,1\]] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] [:case=710;type="f16";wgSize=[64,2,1\]] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] [:case=710;type="f32";wgSize=[128,1,1\]] @@ -8869,50 +8885,42 @@ [:case=711;type="f16";wgSize=[128,1,1\]] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] [:case=711;type="f16";wgSize=[64,2,1\]] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] [:case=711;type="f32";wgSize=[128,1,1\]] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] [:case=711;type="f32";wgSize=[64,2,1\]] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] [:case=712;type="f16";wgSize=[128,1,1\]] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] [:case=712;type="f16";wgSize=[64,2,1\]] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] [:case=712;type="f32";wgSize=[128,1,1\]] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] [:case=712;type="f32";wgSize=[64,2,1\]] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] [:case=713;type="f16";wgSize=[128,1,1\]] @@ -8929,8 +8937,7 @@ [:case=713;type="f32";wgSize=[128,1,1\]] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] [:case=713;type="f32";wgSize=[64,2,1\]] @@ -9543,7 +9550,7 @@ [:case=737;type="f16";wgSize=[128,1,1\]] expected: - if os == "win" and debug: [TIMEOUT, NOTRUN] + if os == "win" and debug: [PASS, TIMEOUT, NOTRUN] if os == "linux" and debug: [TIMEOUT, NOTRUN] if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/call/builtin/textureDimensions/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/call/builtin/textureDimensions/cts.https.html.ini index 1347bbae4763..f508feb6a325 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/call/builtin/textureDimensions/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/call/builtin/textureDimensions/cts.https.html.ini @@ -250,6 +250,8 @@ if os == "win" and debug: [PASS, FAIL] [:format="depth24plus";aspect="all";samples=4] + expected: + if os == "win" and debug: [PASS, FAIL] [:format="depth24plus-stencil8";aspect="depth-only";samples=1] expected: @@ -257,19 +259,23 @@ [:format="depth24plus-stencil8";aspect="depth-only";samples=4] expected: - if os == "win" and not debug: [PASS, FAIL] + if os == "win": [PASS, FAIL] [:format="depth24plus-stencil8";aspect="stencil-only";samples=1] expected: - if os == "win" and not debug: [PASS, FAIL] + if os == "win": [PASS, FAIL] [:format="depth24plus-stencil8";aspect="stencil-only";samples=4] expected: - if os == "win" and not debug: [PASS, FAIL] + if os == "win": [PASS, FAIL] [:format="depth32float";aspect="all";samples=1] + expected: + if os == "win" and debug: [PASS, FAIL] [:format="depth32float";aspect="all";samples=4] + expected: + if os == "win" and debug: [PASS, FAIL] [:format="depth32float-stencil8";aspect="depth-only";samples=1] expected: diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/call/builtin/textureGather/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/call/builtin/textureGather/cts.https.html.ini index f6fb0c4d16fc..c59dcfe31a3f 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/call/builtin/textureGather/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/call/builtin/textureGather/cts.https.html.ini @@ -4647,20 +4647,17 @@ [:stage="f";format="bgra8unorm-srgb";filt="linear";mode="c"] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:stage="f";format="bgra8unorm-srgb";filt="linear";mode="m"] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:stage="f";format="bgra8unorm-srgb";filt="linear";mode="r"] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:stage="f";format="bgra8unorm-srgb";filt="nearest";mode="c"] @@ -5070,74 +5067,62 @@ [:stage="f";format="r16float";filt="linear";mode="c"] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:stage="f";format="r16float";filt="linear";mode="m"] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:stage="f";format="r16float";filt="linear";mode="r"] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:stage="f";format="r16float";filt="nearest";mode="c"] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:stage="f";format="r16float";filt="nearest";mode="m"] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:stage="f";format="r16float";filt="nearest";mode="r"] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:stage="f";format="r16sint";filt="nearest";mode="c"] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:stage="f";format="r16sint";filt="nearest";mode="m"] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:stage="f";format="r16sint";filt="nearest";mode="r"] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:stage="f";format="r16uint";filt="nearest";mode="c"] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:stage="f";format="r16uint";filt="nearest";mode="m"] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:stage="f";format="r16uint";filt="nearest";mode="r"] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:stage="f";format="r32float";filt="linear";mode="c"] @@ -5371,8 +5356,7 @@ [:stage="f";format="rg16uint";filt="nearest";mode="c"] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:stage="f";format="rg16uint";filt="nearest";mode="m"] @@ -5383,8 +5367,7 @@ [:stage="f";format="rg16uint";filt="nearest";mode="r"] expected: - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [PASS, TIMEOUT, NOTRUN] if os == "mac": [PASS, TIMEOUT, NOTRUN] [:stage="f";format="rg32float";filt="linear";mode="c"] @@ -8512,6 +8495,7 @@ [:stage="v";format="rgba16sint";filt="nearest";mode="m"] expected: if os == "win" and debug: [TIMEOUT, NOTRUN] + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] if os == "linux": [TIMEOUT, NOTRUN] if os == "mac": [TIMEOUT, NOTRUN] @@ -8567,13 +8551,21 @@ expected: [TIMEOUT, NOTRUN] [:stage="v";format="rgba32uint";filt="nearest";mode="c"] - expected: [TIMEOUT, NOTRUN] + expected: + if os == "win" and debug: [TIMEOUT, NOTRUN] + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [TIMEOUT, NOTRUN] + if os == "mac": [TIMEOUT, NOTRUN] [:stage="v";format="rgba32uint";filt="nearest";mode="m"] expected: [TIMEOUT, NOTRUN] [:stage="v";format="rgba32uint";filt="nearest";mode="r"] - expected: [TIMEOUT, NOTRUN] + expected: + if os == "win" and debug: [TIMEOUT, NOTRUN] + if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] + if os == "linux": [TIMEOUT, NOTRUN] + if os == "mac": [TIMEOUT, NOTRUN] [:stage="v";format="rgba8sint";filt="nearest";mode="c"] expected: diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/call/builtin/textureSampleCompareLevel/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/call/builtin/textureSampleCompareLevel/cts.https.html.ini index 7067f6fbcb9e..ea2e848595aa 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/call/builtin/textureSampleCompareLevel/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/call/builtin/textureSampleCompareLevel/cts.https.html.ini @@ -2,7 +2,7 @@ tags: [webgpu, webgpu-long] implementation-status: backlog expected: - if os == "win" and debug: CRASH + if os == "win" and debug: [OK, CRASH] if os == "linux" and debug: [OK, CRASH] if os == "mac": CRASH [:stage="c";format="depth16unorm";filt="linear";modeU="c";modeV="c";offset=false] diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/call/builtin/textureSampleLevel/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/call/builtin/textureSampleLevel/cts.https.html.ini index b0241eb3a110..b991cf0cd08e 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/call/builtin/textureSampleLevel/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/call/builtin/textureSampleLevel/cts.https.html.ini @@ -275,9 +275,7 @@ tags: [webgpu, webgpu-long] implementation-status: backlog expected: - if os == "win" and debug: CRASH - if os == "linux" and debug: CRASH - if os == "mac": CRASH + if debug: CRASH [:stage="c";format="depth16unorm";viewDimension="cube";mode="c"] expected: if not debug: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/unary/i32_conversion/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/unary/i32_conversion/cts.https.html.ini index 49250ea32543..08f0b84fa7f1 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/unary/i32_conversion/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/unary/i32_conversion/cts.https.html.ini @@ -1,16 +1,11 @@ [cts.https.html?q=webgpu:shader,execution,expression,unary,i32_conversion:abstract_float:*] - implementation-status: backlog [:inputSource="const";vectorize="_undef_"] - expected: FAIL [:inputSource="const";vectorize=2] - expected: FAIL [:inputSource="const";vectorize=3] - expected: FAIL [:inputSource="const";vectorize=4] - expected: FAIL [cts.https.html?q=webgpu:shader,execution,expression,unary,i32_conversion:abstract_int:*] @@ -98,65 +93,54 @@ [cts.https.html?q=webgpu:shader,execution,expression,unary,i32_conversion:f32:*] - implementation-status: - if os == "linux": backlog - if os == "mac": backlog + implementation-status: backlog [:inputSource="const";vectorize="_undef_"] + expected: FAIL [:inputSource="const";vectorize=2] + expected: FAIL [:inputSource="const";vectorize=3] + expected: FAIL [:inputSource="const";vectorize=4] + expected: FAIL [:inputSource="storage_r";vectorize="_undef_"] - expected: - if os == "linux": FAIL + expected: FAIL [:inputSource="storage_r";vectorize=2] - expected: - if os == "linux": FAIL + expected: FAIL [:inputSource="storage_r";vectorize=3] - expected: - if os == "linux": FAIL + expected: FAIL [:inputSource="storage_r";vectorize=4] - expected: - if os == "linux": FAIL + expected: FAIL [:inputSource="storage_rw";vectorize="_undef_"] - expected: - if os == "linux": FAIL + expected: FAIL [:inputSource="storage_rw";vectorize=2] - expected: - if os == "linux": FAIL + expected: FAIL [:inputSource="storage_rw";vectorize=3] - expected: - if os == "linux": FAIL + expected: FAIL [:inputSource="storage_rw";vectorize=4] - expected: - if os == "linux": FAIL + expected: FAIL [:inputSource="uniform";vectorize="_undef_"] - expected: - if os == "linux": FAIL - if os == "mac": FAIL + expected: FAIL [:inputSource="uniform";vectorize=2] - expected: - if os == "linux": FAIL + expected: FAIL [:inputSource="uniform";vectorize=3] - expected: - if os == "linux": FAIL + expected: FAIL [:inputSource="uniform";vectorize=4] - expected: - if os == "linux": FAIL + expected: FAIL [cts.https.html?q=webgpu:shader,execution,expression,unary,i32_conversion:i32:*] diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/unary/u32_conversion/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/unary/u32_conversion/cts.https.html.ini index a9a77b396ec7..f45f5dea68b7 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/unary/u32_conversion/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/expression/unary/u32_conversion/cts.https.html.ini @@ -1,16 +1,11 @@ [cts.https.html?q=webgpu:shader,execution,expression,unary,u32_conversion:abstract_float:*] - implementation-status: backlog [:inputSource="const";vectorize="_undef_"] - expected: FAIL [:inputSource="const";vectorize=2] - expected: FAIL [:inputSource="const";vectorize=3] - expected: FAIL [:inputSource="const";vectorize=4] - expected: FAIL [cts.https.html?q=webgpu:shader,execution,expression,unary,u32_conversion:abstract_int:*] @@ -60,8 +55,6 @@ [cts.https.html?q=webgpu:shader,execution,expression,unary,u32_conversion:f16:*] implementation-status: if os == "mac": backlog - expected: - if os == "mac": CRASH [:inputSource="const";vectorize="_undef_"] [:inputSource="const";vectorize=2] @@ -87,8 +80,12 @@ [:inputSource="storage_rw";vectorize=4] [:inputSource="uniform";vectorize="_undef_"] + expected: + if os == "mac": FAIL [:inputSource="uniform";vectorize=2] + expected: + if os == "mac": FAIL [:inputSource="uniform";vectorize=3] @@ -96,65 +93,54 @@ [cts.https.html?q=webgpu:shader,execution,expression,unary,u32_conversion:f32:*] - implementation-status: - if os == "linux": backlog - if os == "mac": backlog + implementation-status: backlog [:inputSource="const";vectorize="_undef_"] + expected: FAIL [:inputSource="const";vectorize=2] + expected: FAIL [:inputSource="const";vectorize=3] + expected: FAIL [:inputSource="const";vectorize=4] + expected: FAIL [:inputSource="storage_r";vectorize="_undef_"] - expected: - if os == "linux": FAIL + expected: FAIL [:inputSource="storage_r";vectorize=2] - expected: - if os == "linux": FAIL + expected: FAIL [:inputSource="storage_r";vectorize=3] - expected: - if os == "linux": FAIL + expected: FAIL [:inputSource="storage_r";vectorize=4] - expected: - if os == "linux": FAIL + expected: FAIL [:inputSource="storage_rw";vectorize="_undef_"] - expected: - if os == "linux": FAIL + expected: FAIL [:inputSource="storage_rw";vectorize=2] - expected: - if os == "linux": FAIL + expected: FAIL [:inputSource="storage_rw";vectorize=3] - expected: - if os == "linux": FAIL + expected: FAIL [:inputSource="storage_rw";vectorize=4] - expected: - if os == "linux": FAIL + expected: FAIL [:inputSource="uniform";vectorize="_undef_"] - expected: - if os == "linux": FAIL - if os == "mac": FAIL + expected: FAIL [:inputSource="uniform";vectorize=2] - expected: - if os == "linux": FAIL + expected: FAIL [:inputSource="uniform";vectorize=3] - expected: - if os == "linux": FAIL + expected: FAIL [:inputSource="uniform";vectorize=4] - expected: - if os == "linux": FAIL + expected: FAIL [cts.https.html?q=webgpu:shader,execution,expression,unary,u32_conversion:i32:*] diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/limits/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/limits/cts.https.html.ini index 601d8bc616df..2c0eccd59c46 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/limits/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/execution/limits/cts.https.html.ini @@ -25,9 +25,7 @@ [cts.https.html?q=webgpu:shader,execution,limits:nesting_depth_braces:*] tags: [webgpu, webgpu-long] - implementation-status: backlog [:] - expected: FAIL [cts.https.html?q=webgpu:shader,execution,limits:nesting_depth_composite_array:*] diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/decl/override/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/decl/override/cts.https.html.ini index d1c6ccd99984..61c7d294f3b0 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/decl/override/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/decl/override/cts.https.html.ini @@ -57,8 +57,6 @@ [cts.https.html?q=webgpu:shader,validation,decl,override:type:*] - implementation-status: - if os == "mac": backlog [:case="abs_float_conversion"] [:case="abs_int_conversion"] @@ -70,8 +68,6 @@ [:case="bool"] [:case="f16"] - expected: - if os == "mac": FAIL [:case="f32"] diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/binary/add_sub_mul/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/binary/add_sub_mul/cts.https.html.ini index a0457bfb6dc7..e8561c4eefa1 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/binary/add_sub_mul/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/binary/add_sub_mul/cts.https.html.ini @@ -1654,6 +1654,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,binary,add_sub_mul:scalar_vector_out_of_range:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:op="add";lhs="f16";rhs="f16"] expected: if os == "mac": FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/binary/div_rem/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/binary/div_rem/cts.https.html.ini index d132bb36b419..0cf53afcfcf5 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/binary/div_rem/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/binary/div_rem/cts.https.html.ini @@ -1736,6 +1736,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,binary,div_rem:scalar_vector_out_of_range:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:op="div";lhs="f16";rhs="f16"] expected: if os == "mac": FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/abs/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/abs/cts.https.html.ini index 1e46a71d7dae..a2d5871f9791 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/abs/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/abs/cts.https.html.ini @@ -40,6 +40,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,abs:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/acos/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/acos/cts.https.html.ini index 11b003a50b8a..ef05b2fc9431 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/acos/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/acos/cts.https.html.ini @@ -113,6 +113,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,acos:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: if os == "win" and debug: [PASS, FAIL] diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/acosh/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/acosh/cts.https.html.ini index 4b48e64fff2d..24364171de81 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/acosh/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/acosh/cts.https.html.ini @@ -67,6 +67,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,acosh:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/asin/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/asin/cts.https.html.ini index 3d1e5812c332..8e9a8e0b7a00 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/asin/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/asin/cts.https.html.ini @@ -71,6 +71,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,asin:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] [:stage="constant";type="abstract-int"] diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/asinh/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/asinh/cts.https.html.ini index f9655733b91b..f3ce348986da 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/asinh/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/asinh/cts.https.html.ini @@ -67,6 +67,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,asinh:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/atan/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/atan/cts.https.html.ini index 2c6383d0eeaf..1e02ad59fa8e 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/atan/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/atan/cts.https.html.ini @@ -67,6 +67,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,atan:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/atan2/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/atan2/cts.https.html.ini index fade29ed7591..170dd515417c 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/atan2/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/atan2/cts.https.html.ini @@ -231,6 +231,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,atan2:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/atanh/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/atanh/cts.https.html.ini index 03881c1e6d92..04c10ded3b82 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/atanh/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/atanh/cts.https.html.ini @@ -75,6 +75,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,atanh:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] [:stage="constant";type="abstract-int"] diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/ceil/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/ceil/cts.https.html.ini index aef0b406930b..0fce7d605ff3 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/ceil/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/ceil/cts.https.html.ini @@ -66,6 +66,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,ceil:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/clamp/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/clamp/cts.https.html.ini index 3e64cbc3b15a..b6f8532669a3 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/clamp/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/clamp/cts.https.html.ini @@ -33,6 +33,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,clamp:low_high:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:type="f16";lowStage="constant";highStage="constant"] expected: if os == "mac": FAIL @@ -533,6 +535,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,clamp:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/cos/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/cos/cts.https.html.ini index d9c4baa433a5..f472ed22cf1c 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/cos/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/cos/cts.https.html.ini @@ -50,6 +50,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,cos:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/cosh/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/cosh/cts.https.html.ini index 4248ac5a1754..daebb7ac2f8a 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/cosh/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/cosh/cts.https.html.ini @@ -40,6 +40,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,cosh:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] [:stage="constant";type="abstract-int"] diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/cross/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/cross/cts.https.html.ini index 36da60fc451a..78f7cf6a6c35 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/cross/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/cross/cts.https.html.ini @@ -31,7 +31,6 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,cross:must_use:*] implementation-status: backlog [:use=false] - expected: FAIL [:use=true] expected: FAIL @@ -39,6 +38,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,cross:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="vec3%3Cabstract-float%3E"] expected: FAIL @@ -50,11 +51,13 @@ if os == "mac": FAIL [:stage="constant";type="vec3%3Cf32%3E"] - expected: FAIL + expected: + if os == "mac": FAIL [:stage="override";type="vec3%3Cf16%3E"] expected: if os == "mac": FAIL [:stage="override";type="vec3%3Cf32%3E"] - expected: FAIL + expected: + if os == "mac": FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/degrees/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/degrees/cts.https.html.ini index 9487c62080aa..13fb00f995b5 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/degrees/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/degrees/cts.https.html.ini @@ -50,6 +50,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,degrees:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/distance/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/distance/cts.https.html.ini index c6908d57d0ec..75ee8dfdc5c4 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/distance/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/distance/cts.https.html.ini @@ -67,6 +67,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,distance:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/dot/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/dot/cts.https.html.ini index 07164533a81c..1ade2deae53d 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/dot/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/dot/cts.https.html.ini @@ -56,6 +56,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,dot:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="vec2%3Cabstract-float%3E"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/exp/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/exp/cts.https.html.ini index a568c852f56b..d8464fc8b229 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/exp/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/exp/cts.https.html.ini @@ -40,6 +40,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,exp:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/exp2/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/exp2/cts.https.html.ini index 08d9d739bf91..9635bd0a26ab 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/exp2/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/exp2/cts.https.html.ini @@ -40,6 +40,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,exp2:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/faceForward/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/faceForward/cts.https.html.ini index b8cb1044d804..4bae9922de8c 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/faceForward/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/faceForward/cts.https.html.ini @@ -91,6 +91,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,faceForward:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="vec2%3Cabstract-float%3E"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/floor/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/floor/cts.https.html.ini index e6f6d163ddc4..030a0a2ef217 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/floor/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/floor/cts.https.html.ini @@ -50,6 +50,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,floor:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/fma/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/fma/cts.https.html.ini index 09a55fbdb394..1091f4a794aa 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/fma/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/fma/cts.https.html.ini @@ -90,6 +90,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,fma:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="vec2%3Cabstract-float%3E"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/fract/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/fract/cts.https.html.ini index c5e36fb0711b..05889d838db3 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/fract/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/fract/cts.https.html.ini @@ -40,6 +40,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,fract:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/inverseSqrt/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/inverseSqrt/cts.https.html.ini index 621d293ad66a..cadc63251655 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/inverseSqrt/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/inverseSqrt/cts.https.html.ini @@ -40,6 +40,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,inverseSqrt:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/ldexp/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/ldexp/cts.https.html.ini index 88d48cbcbe8c..1041d1c6669b 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/ldexp/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/ldexp/cts.https.html.ini @@ -156,6 +156,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,ldexp:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";typeA="abstract-float";typeB="abstract-int"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/length/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/length/cts.https.html.ini index 8e75a7a5e580..3302f4957197 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/length/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/length/cts.https.html.ini @@ -56,6 +56,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,length:scalar:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL @@ -79,6 +81,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,length:vec2:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="vec2%3Cabstract-float%3E"] expected: FAIL @@ -102,6 +106,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,length:vec3:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="vec3%3Cabstract-float%3E"] expected: FAIL @@ -126,6 +132,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,length:vec4:*] tags: [webgpu, webgpu-long] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="vec4%3Cabstract-float%3E"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/log/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/log/cts.https.html.ini index 4dc92c2ad1b0..2fdf8cdc7fbf 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/log/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/log/cts.https.html.ini @@ -51,6 +51,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,log:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/log2/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/log2/cts.https.html.ini index 858dc1154d30..7d01797f0218 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/log2/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/log2/cts.https.html.ini @@ -50,6 +50,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,log2:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/max/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/max/cts.https.html.ini index 6e31ec673d3a..46a56e71466b 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/max/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/max/cts.https.html.ini @@ -34,6 +34,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,max:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/min/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/min/cts.https.html.ini index ba0a83904a8a..15ce96d3bfdb 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/min/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/min/cts.https.html.ini @@ -32,6 +32,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,min:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/mix/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/mix/cts.https.html.ini index 5bc08fdd8769..ce8f18708ba1 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/mix/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/mix/cts.https.html.ini @@ -91,6 +91,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,mix:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="vec2%3Cabstract-float%3E"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/modf/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/modf/cts.https.html.ini index cdaac1eff3a6..39c1b509933c 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/modf/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/modf/cts.https.html.ini @@ -68,6 +68,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,modf:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/normalize/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/normalize/cts.https.html.ini index cdd07c571659..530e033f00e4 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/normalize/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/normalize/cts.https.html.ini @@ -56,6 +56,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,normalize:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="vec2%3Cabstract-float%3E"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/pow/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/pow/cts.https.html.ini index 2cb86422e2cf..28e19fc96dd6 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/pow/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/pow/cts.https.html.ini @@ -90,6 +90,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,pow:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/radians/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/radians/cts.https.html.ini index 966d69567522..befab4b7d625 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/radians/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/radians/cts.https.html.ini @@ -50,6 +50,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,radians:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/reflect/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/reflect/cts.https.html.ini index 919ee0365e84..f1f9046bbde7 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/reflect/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/reflect/cts.https.html.ini @@ -35,6 +35,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,reflect:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="vec2%3Cabstract-float%3E"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/refract/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/refract/cts.https.html.ini index 10abe82eb910..2af4b35abf43 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/refract/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/refract/cts.https.html.ini @@ -163,6 +163,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,refract:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="vec2%3Cabstract-float%3E"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/round/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/round/cts.https.html.ini index 6b21a6c473df..daa65163ee24 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/round/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/round/cts.https.html.ini @@ -100,6 +100,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,round:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/saturate/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/saturate/cts.https.html.ini index 2e5f7497aa00..2640ebd90382 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/saturate/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/saturate/cts.https.html.ini @@ -66,6 +66,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,saturate:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/sign/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/sign/cts.https.html.ini index 9f9f4a63b0b1..72d4abe730ec 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/sign/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/sign/cts.https.html.ini @@ -32,6 +32,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,sign:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/sin/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/sin/cts.https.html.ini index 10798df1c27f..c3f555832e99 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/sin/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/sin/cts.https.html.ini @@ -50,6 +50,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,sin:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/sinh/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/sinh/cts.https.html.ini index a6aa4d4b3119..062ee3b1c2a4 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/sinh/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/sinh/cts.https.html.ini @@ -40,6 +40,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,sinh:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] [:stage="constant";type="abstract-int"] diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/smoothstep/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/smoothstep/cts.https.html.ini index 2a7025ca2dbd..06adacf1cba1 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/smoothstep/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/smoothstep/cts.https.html.ini @@ -152,6 +152,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,smoothstep:partial_eval_errors:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:lowStage="constant";highStage="constant";type="f16"] expected: if os == "mac": FAIL @@ -387,6 +389,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,smoothstep:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/sqrt/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/sqrt/cts.https.html.ini index ceeb37a3b011..00752d92665d 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/sqrt/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/sqrt/cts.https.html.ini @@ -50,6 +50,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,sqrt:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/step/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/step/cts.https.html.ini index dd8574672dc1..93bfea2ba2cb 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/step/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/step/cts.https.html.ini @@ -64,6 +64,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,step:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/tan/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/tan/cts.https.html.ini index 4c9402e59004..f4ce2158778a 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/tan/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/tan/cts.https.html.ini @@ -40,6 +40,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,tan:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/tanh/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/tanh/cts.https.html.ini index 14a38a1b107c..baee9eec1b6b 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/tanh/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/tanh/cts.https.html.ini @@ -40,6 +40,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,tanh:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/textureDimensions/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/textureDimensions/cts.https.html.ini index 7f21a74ac167..56ec9188f1ad 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/textureDimensions/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/textureDimensions/cts.https.html.ini @@ -1,774 +1,563 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,textureDimensions:level_argument,non_storage:*] - implementation-status: backlog [:textureType="texture_1d";levelType="abstract-float"] - expected: FAIL [:textureType="texture_1d";levelType="abstract-int"] [:textureType="texture_1d";levelType="bool"] - expected: FAIL [:textureType="texture_1d";levelType="f16"] [:textureType="texture_1d";levelType="f32"] - expected: FAIL [:textureType="texture_1d";levelType="i32"] [:textureType="texture_1d";levelType="u32"] [:textureType="texture_1d";levelType="vec2%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_1d";levelType="vec2%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_1d";levelType="vec2%3Cbool%3E"] - expected: FAIL [:textureType="texture_1d";levelType="vec2%3Cf16%3E"] [:textureType="texture_1d";levelType="vec2%3Cf32%3E"] - expected: FAIL [:textureType="texture_1d";levelType="vec2%3Ci32%3E"] - expected: FAIL [:textureType="texture_1d";levelType="vec2%3Cu32%3E"] - expected: FAIL [:textureType="texture_1d";levelType="vec3%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_1d";levelType="vec3%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_1d";levelType="vec3%3Cbool%3E"] - expected: FAIL [:textureType="texture_1d";levelType="vec3%3Cf16%3E"] [:textureType="texture_1d";levelType="vec3%3Cf32%3E"] - expected: FAIL [:textureType="texture_1d";levelType="vec3%3Ci32%3E"] - expected: FAIL [:textureType="texture_1d";levelType="vec3%3Cu32%3E"] - expected: FAIL [:textureType="texture_1d";levelType="vec4%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_1d";levelType="vec4%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_1d";levelType="vec4%3Cbool%3E"] - expected: FAIL [:textureType="texture_1d";levelType="vec4%3Cf16%3E"] [:textureType="texture_1d";levelType="vec4%3Cf32%3E"] - expected: FAIL [:textureType="texture_1d";levelType="vec4%3Ci32%3E"] - expected: FAIL [:textureType="texture_1d";levelType="vec4%3Cu32%3E"] - expected: FAIL [:textureType="texture_2d";levelType="abstract-float"] - expected: FAIL [:textureType="texture_2d";levelType="abstract-int"] [:textureType="texture_2d";levelType="bool"] - expected: FAIL [:textureType="texture_2d";levelType="f16"] [:textureType="texture_2d";levelType="f32"] - expected: FAIL [:textureType="texture_2d";levelType="i32"] [:textureType="texture_2d";levelType="u32"] [:textureType="texture_2d";levelType="vec2%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_2d";levelType="vec2%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_2d";levelType="vec2%3Cbool%3E"] - expected: FAIL [:textureType="texture_2d";levelType="vec2%3Cf16%3E"] [:textureType="texture_2d";levelType="vec2%3Cf32%3E"] - expected: FAIL [:textureType="texture_2d";levelType="vec2%3Ci32%3E"] - expected: FAIL [:textureType="texture_2d";levelType="vec2%3Cu32%3E"] - expected: FAIL [:textureType="texture_2d";levelType="vec3%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_2d";levelType="vec3%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_2d";levelType="vec3%3Cbool%3E"] - expected: FAIL [:textureType="texture_2d";levelType="vec3%3Cf16%3E"] [:textureType="texture_2d";levelType="vec3%3Cf32%3E"] - expected: FAIL [:textureType="texture_2d";levelType="vec3%3Ci32%3E"] - expected: FAIL [:textureType="texture_2d";levelType="vec3%3Cu32%3E"] - expected: FAIL [:textureType="texture_2d";levelType="vec4%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_2d";levelType="vec4%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_2d";levelType="vec4%3Cbool%3E"] - expected: FAIL [:textureType="texture_2d";levelType="vec4%3Cf16%3E"] [:textureType="texture_2d";levelType="vec4%3Cf32%3E"] - expected: FAIL [:textureType="texture_2d";levelType="vec4%3Ci32%3E"] - expected: FAIL [:textureType="texture_2d";levelType="vec4%3Cu32%3E"] - expected: FAIL [:textureType="texture_2d_array";levelType="abstract-float"] - expected: FAIL [:textureType="texture_2d_array";levelType="abstract-int"] [:textureType="texture_2d_array";levelType="bool"] - expected: FAIL [:textureType="texture_2d_array";levelType="f16"] [:textureType="texture_2d_array";levelType="f32"] - expected: FAIL [:textureType="texture_2d_array";levelType="i32"] [:textureType="texture_2d_array";levelType="u32"] [:textureType="texture_2d_array";levelType="vec2%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_2d_array";levelType="vec2%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_2d_array";levelType="vec2%3Cbool%3E"] - expected: FAIL [:textureType="texture_2d_array";levelType="vec2%3Cf16%3E"] [:textureType="texture_2d_array";levelType="vec2%3Cf32%3E"] - expected: FAIL [:textureType="texture_2d_array";levelType="vec2%3Ci32%3E"] - expected: FAIL [:textureType="texture_2d_array";levelType="vec2%3Cu32%3E"] - expected: FAIL [:textureType="texture_2d_array";levelType="vec3%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_2d_array";levelType="vec3%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_2d_array";levelType="vec3%3Cbool%3E"] - expected: FAIL [:textureType="texture_2d_array";levelType="vec3%3Cf16%3E"] [:textureType="texture_2d_array";levelType="vec3%3Cf32%3E"] - expected: FAIL [:textureType="texture_2d_array";levelType="vec3%3Ci32%3E"] - expected: FAIL [:textureType="texture_2d_array";levelType="vec3%3Cu32%3E"] - expected: FAIL [:textureType="texture_2d_array";levelType="vec4%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_2d_array";levelType="vec4%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_2d_array";levelType="vec4%3Cbool%3E"] - expected: FAIL [:textureType="texture_2d_array";levelType="vec4%3Cf16%3E"] [:textureType="texture_2d_array";levelType="vec4%3Cf32%3E"] - expected: FAIL [:textureType="texture_2d_array";levelType="vec4%3Ci32%3E"] - expected: FAIL [:textureType="texture_2d_array";levelType="vec4%3Cu32%3E"] - expected: FAIL [:textureType="texture_3d";levelType="abstract-float"] - expected: FAIL [:textureType="texture_3d";levelType="abstract-int"] [:textureType="texture_3d";levelType="bool"] - expected: FAIL [:textureType="texture_3d";levelType="f16"] [:textureType="texture_3d";levelType="f32"] - expected: FAIL [:textureType="texture_3d";levelType="i32"] [:textureType="texture_3d";levelType="u32"] [:textureType="texture_3d";levelType="vec2%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_3d";levelType="vec2%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_3d";levelType="vec2%3Cbool%3E"] - expected: FAIL [:textureType="texture_3d";levelType="vec2%3Cf16%3E"] [:textureType="texture_3d";levelType="vec2%3Cf32%3E"] - expected: FAIL [:textureType="texture_3d";levelType="vec2%3Ci32%3E"] - expected: FAIL [:textureType="texture_3d";levelType="vec2%3Cu32%3E"] - expected: FAIL [:textureType="texture_3d";levelType="vec3%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_3d";levelType="vec3%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_3d";levelType="vec3%3Cbool%3E"] - expected: FAIL [:textureType="texture_3d";levelType="vec3%3Cf16%3E"] [:textureType="texture_3d";levelType="vec3%3Cf32%3E"] - expected: FAIL [:textureType="texture_3d";levelType="vec3%3Ci32%3E"] - expected: FAIL [:textureType="texture_3d";levelType="vec3%3Cu32%3E"] - expected: FAIL [:textureType="texture_3d";levelType="vec4%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_3d";levelType="vec4%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_3d";levelType="vec4%3Cbool%3E"] - expected: FAIL [:textureType="texture_3d";levelType="vec4%3Cf16%3E"] [:textureType="texture_3d";levelType="vec4%3Cf32%3E"] - expected: FAIL [:textureType="texture_3d";levelType="vec4%3Ci32%3E"] - expected: FAIL [:textureType="texture_3d";levelType="vec4%3Cu32%3E"] - expected: FAIL [:textureType="texture_cube";levelType="abstract-float"] - expected: FAIL [:textureType="texture_cube";levelType="abstract-int"] [:textureType="texture_cube";levelType="bool"] - expected: FAIL [:textureType="texture_cube";levelType="f16"] [:textureType="texture_cube";levelType="f32"] - expected: FAIL [:textureType="texture_cube";levelType="i32"] [:textureType="texture_cube";levelType="u32"] [:textureType="texture_cube";levelType="vec2%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_cube";levelType="vec2%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_cube";levelType="vec2%3Cbool%3E"] - expected: FAIL [:textureType="texture_cube";levelType="vec2%3Cf16%3E"] [:textureType="texture_cube";levelType="vec2%3Cf32%3E"] - expected: FAIL [:textureType="texture_cube";levelType="vec2%3Ci32%3E"] - expected: FAIL [:textureType="texture_cube";levelType="vec2%3Cu32%3E"] - expected: FAIL [:textureType="texture_cube";levelType="vec3%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_cube";levelType="vec3%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_cube";levelType="vec3%3Cbool%3E"] - expected: FAIL [:textureType="texture_cube";levelType="vec3%3Cf16%3E"] [:textureType="texture_cube";levelType="vec3%3Cf32%3E"] - expected: FAIL [:textureType="texture_cube";levelType="vec3%3Ci32%3E"] - expected: FAIL [:textureType="texture_cube";levelType="vec3%3Cu32%3E"] - expected: FAIL [:textureType="texture_cube";levelType="vec4%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_cube";levelType="vec4%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_cube";levelType="vec4%3Cbool%3E"] - expected: FAIL [:textureType="texture_cube";levelType="vec4%3Cf16%3E"] [:textureType="texture_cube";levelType="vec4%3Cf32%3E"] - expected: FAIL [:textureType="texture_cube";levelType="vec4%3Ci32%3E"] - expected: FAIL [:textureType="texture_cube";levelType="vec4%3Cu32%3E"] - expected: FAIL [:textureType="texture_cube_array";levelType="abstract-float"] - expected: FAIL [:textureType="texture_cube_array";levelType="abstract-int"] [:textureType="texture_cube_array";levelType="bool"] - expected: FAIL [:textureType="texture_cube_array";levelType="f16"] [:textureType="texture_cube_array";levelType="f32"] - expected: FAIL [:textureType="texture_cube_array";levelType="i32"] [:textureType="texture_cube_array";levelType="u32"] [:textureType="texture_cube_array";levelType="vec2%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_cube_array";levelType="vec2%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_cube_array";levelType="vec2%3Cbool%3E"] - expected: FAIL [:textureType="texture_cube_array";levelType="vec2%3Cf16%3E"] [:textureType="texture_cube_array";levelType="vec2%3Cf32%3E"] - expected: FAIL [:textureType="texture_cube_array";levelType="vec2%3Ci32%3E"] - expected: FAIL [:textureType="texture_cube_array";levelType="vec2%3Cu32%3E"] - expected: FAIL [:textureType="texture_cube_array";levelType="vec3%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_cube_array";levelType="vec3%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_cube_array";levelType="vec3%3Cbool%3E"] - expected: FAIL [:textureType="texture_cube_array";levelType="vec3%3Cf16%3E"] [:textureType="texture_cube_array";levelType="vec3%3Cf32%3E"] - expected: FAIL [:textureType="texture_cube_array";levelType="vec3%3Ci32%3E"] - expected: FAIL [:textureType="texture_cube_array";levelType="vec3%3Cu32%3E"] - expected: FAIL [:textureType="texture_cube_array";levelType="vec4%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_cube_array";levelType="vec4%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_cube_array";levelType="vec4%3Cbool%3E"] - expected: FAIL [:textureType="texture_cube_array";levelType="vec4%3Cf16%3E"] [:textureType="texture_cube_array";levelType="vec4%3Cf32%3E"] - expected: FAIL [:textureType="texture_cube_array";levelType="vec4%3Ci32%3E"] - expected: FAIL [:textureType="texture_cube_array";levelType="vec4%3Cu32%3E"] - expected: FAIL [:textureType="texture_depth_2d";levelType="abstract-float"] - expected: FAIL [:textureType="texture_depth_2d";levelType="abstract-int"] [:textureType="texture_depth_2d";levelType="bool"] - expected: FAIL [:textureType="texture_depth_2d";levelType="f16"] [:textureType="texture_depth_2d";levelType="f32"] - expected: FAIL [:textureType="texture_depth_2d";levelType="i32"] [:textureType="texture_depth_2d";levelType="u32"] [:textureType="texture_depth_2d";levelType="vec2%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_depth_2d";levelType="vec2%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_depth_2d";levelType="vec2%3Cbool%3E"] - expected: FAIL [:textureType="texture_depth_2d";levelType="vec2%3Cf16%3E"] [:textureType="texture_depth_2d";levelType="vec2%3Cf32%3E"] - expected: FAIL [:textureType="texture_depth_2d";levelType="vec2%3Ci32%3E"] - expected: FAIL [:textureType="texture_depth_2d";levelType="vec2%3Cu32%3E"] - expected: FAIL [:textureType="texture_depth_2d";levelType="vec3%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_depth_2d";levelType="vec3%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_depth_2d";levelType="vec3%3Cbool%3E"] - expected: FAIL [:textureType="texture_depth_2d";levelType="vec3%3Cf16%3E"] [:textureType="texture_depth_2d";levelType="vec3%3Cf32%3E"] - expected: FAIL [:textureType="texture_depth_2d";levelType="vec3%3Ci32%3E"] - expected: FAIL [:textureType="texture_depth_2d";levelType="vec3%3Cu32%3E"] - expected: FAIL [:textureType="texture_depth_2d";levelType="vec4%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_depth_2d";levelType="vec4%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_depth_2d";levelType="vec4%3Cbool%3E"] - expected: FAIL [:textureType="texture_depth_2d";levelType="vec4%3Cf16%3E"] [:textureType="texture_depth_2d";levelType="vec4%3Cf32%3E"] - expected: FAIL [:textureType="texture_depth_2d";levelType="vec4%3Ci32%3E"] - expected: FAIL [:textureType="texture_depth_2d";levelType="vec4%3Cu32%3E"] - expected: FAIL [:textureType="texture_depth_2d_array";levelType="abstract-float"] - expected: FAIL [:textureType="texture_depth_2d_array";levelType="abstract-int"] [:textureType="texture_depth_2d_array";levelType="bool"] - expected: FAIL [:textureType="texture_depth_2d_array";levelType="f16"] [:textureType="texture_depth_2d_array";levelType="f32"] - expected: FAIL [:textureType="texture_depth_2d_array";levelType="i32"] [:textureType="texture_depth_2d_array";levelType="u32"] [:textureType="texture_depth_2d_array";levelType="vec2%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_depth_2d_array";levelType="vec2%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_depth_2d_array";levelType="vec2%3Cbool%3E"] - expected: FAIL [:textureType="texture_depth_2d_array";levelType="vec2%3Cf16%3E"] [:textureType="texture_depth_2d_array";levelType="vec2%3Cf32%3E"] - expected: FAIL [:textureType="texture_depth_2d_array";levelType="vec2%3Ci32%3E"] - expected: FAIL [:textureType="texture_depth_2d_array";levelType="vec2%3Cu32%3E"] - expected: FAIL [:textureType="texture_depth_2d_array";levelType="vec3%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_depth_2d_array";levelType="vec3%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_depth_2d_array";levelType="vec3%3Cbool%3E"] - expected: FAIL [:textureType="texture_depth_2d_array";levelType="vec3%3Cf16%3E"] [:textureType="texture_depth_2d_array";levelType="vec3%3Cf32%3E"] - expected: FAIL [:textureType="texture_depth_2d_array";levelType="vec3%3Ci32%3E"] - expected: FAIL [:textureType="texture_depth_2d_array";levelType="vec3%3Cu32%3E"] - expected: FAIL [:textureType="texture_depth_2d_array";levelType="vec4%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_depth_2d_array";levelType="vec4%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_depth_2d_array";levelType="vec4%3Cbool%3E"] - expected: FAIL [:textureType="texture_depth_2d_array";levelType="vec4%3Cf16%3E"] [:textureType="texture_depth_2d_array";levelType="vec4%3Cf32%3E"] - expected: FAIL [:textureType="texture_depth_2d_array";levelType="vec4%3Ci32%3E"] - expected: FAIL [:textureType="texture_depth_2d_array";levelType="vec4%3Cu32%3E"] - expected: FAIL [:textureType="texture_depth_cube";levelType="abstract-float"] - expected: FAIL [:textureType="texture_depth_cube";levelType="abstract-int"] [:textureType="texture_depth_cube";levelType="bool"] - expected: FAIL [:textureType="texture_depth_cube";levelType="f16"] [:textureType="texture_depth_cube";levelType="f32"] - expected: FAIL [:textureType="texture_depth_cube";levelType="i32"] [:textureType="texture_depth_cube";levelType="u32"] [:textureType="texture_depth_cube";levelType="vec2%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_depth_cube";levelType="vec2%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_depth_cube";levelType="vec2%3Cbool%3E"] - expected: FAIL [:textureType="texture_depth_cube";levelType="vec2%3Cf16%3E"] [:textureType="texture_depth_cube";levelType="vec2%3Cf32%3E"] - expected: FAIL [:textureType="texture_depth_cube";levelType="vec2%3Ci32%3E"] - expected: FAIL [:textureType="texture_depth_cube";levelType="vec2%3Cu32%3E"] - expected: FAIL [:textureType="texture_depth_cube";levelType="vec3%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_depth_cube";levelType="vec3%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_depth_cube";levelType="vec3%3Cbool%3E"] - expected: FAIL [:textureType="texture_depth_cube";levelType="vec3%3Cf16%3E"] [:textureType="texture_depth_cube";levelType="vec3%3Cf32%3E"] - expected: FAIL [:textureType="texture_depth_cube";levelType="vec3%3Ci32%3E"] - expected: FAIL [:textureType="texture_depth_cube";levelType="vec3%3Cu32%3E"] - expected: FAIL [:textureType="texture_depth_cube";levelType="vec4%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_depth_cube";levelType="vec4%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_depth_cube";levelType="vec4%3Cbool%3E"] - expected: FAIL [:textureType="texture_depth_cube";levelType="vec4%3Cf16%3E"] [:textureType="texture_depth_cube";levelType="vec4%3Cf32%3E"] - expected: FAIL [:textureType="texture_depth_cube";levelType="vec4%3Ci32%3E"] - expected: FAIL [:textureType="texture_depth_cube";levelType="vec4%3Cu32%3E"] - expected: FAIL [:textureType="texture_depth_cube_array";levelType="abstract-float"] - expected: FAIL [:textureType="texture_depth_cube_array";levelType="abstract-int"] [:textureType="texture_depth_cube_array";levelType="bool"] - expected: FAIL [:textureType="texture_depth_cube_array";levelType="f16"] [:textureType="texture_depth_cube_array";levelType="f32"] - expected: FAIL [:textureType="texture_depth_cube_array";levelType="i32"] [:textureType="texture_depth_cube_array";levelType="u32"] [:textureType="texture_depth_cube_array";levelType="vec2%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_depth_cube_array";levelType="vec2%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_depth_cube_array";levelType="vec2%3Cbool%3E"] - expected: FAIL [:textureType="texture_depth_cube_array";levelType="vec2%3Cf16%3E"] [:textureType="texture_depth_cube_array";levelType="vec2%3Cf32%3E"] - expected: FAIL [:textureType="texture_depth_cube_array";levelType="vec2%3Ci32%3E"] - expected: FAIL [:textureType="texture_depth_cube_array";levelType="vec2%3Cu32%3E"] - expected: FAIL [:textureType="texture_depth_cube_array";levelType="vec3%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_depth_cube_array";levelType="vec3%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_depth_cube_array";levelType="vec3%3Cbool%3E"] - expected: FAIL [:textureType="texture_depth_cube_array";levelType="vec3%3Cf16%3E"] [:textureType="texture_depth_cube_array";levelType="vec3%3Cf32%3E"] - expected: FAIL [:textureType="texture_depth_cube_array";levelType="vec3%3Ci32%3E"] - expected: FAIL [:textureType="texture_depth_cube_array";levelType="vec3%3Cu32%3E"] - expected: FAIL [:textureType="texture_depth_cube_array";levelType="vec4%3Cabstract-float%3E"] - expected: FAIL [:textureType="texture_depth_cube_array";levelType="vec4%3Cabstract-int%3E"] - expected: FAIL [:textureType="texture_depth_cube_array";levelType="vec4%3Cbool%3E"] - expected: FAIL [:textureType="texture_depth_cube_array";levelType="vec4%3Cf16%3E"] [:textureType="texture_depth_cube_array";levelType="vec4%3Cf32%3E"] - expected: FAIL [:textureType="texture_depth_cube_array";levelType="vec4%3Ci32%3E"] - expected: FAIL [:textureType="texture_depth_cube_array";levelType="vec4%3Cu32%3E"] - expected: FAIL [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,textureDimensions:must_use:*] diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/transpose/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/transpose/cts.https.html.ini index 93c545117613..09f0fe609926 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/transpose/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/transpose/cts.https.html.ini @@ -1071,6 +1071,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,transpose:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="mat2x2%3Cf16%3E"] expected: if os == "mac": FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/trunc/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/trunc/cts.https.html.ini index 3d847df46e92..ba8f957429f8 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/trunc/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/trunc/cts.https.html.ini @@ -40,6 +40,8 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,trunc:values:*] implementation-status: backlog + expected: + if os == "mac": CRASH [:stage="constant";type="abstract-float"] expected: FAIL diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/value_constructor/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/value_constructor/cts.https.html.ini index 0acc79b0a9b8..42d671228a31 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/value_constructor/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/shader/validation/expression/call/builtin/value_constructor/cts.https.html.ini @@ -452,9 +452,9 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,value_constructor:scalar_zero_value:*] - implementation-status: backlog + implementation-status: + if os == "mac": backlog [:type="bool"] - expected: FAIL [:type="f16"] expected: @@ -506,7 +506,6 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,value_constructor:vector_copy:*] - implementation-status: backlog [:decl_type="abstract-float";value_type="abstract-float"] [:decl_type="abstract-float";value_type="abstract-int"] @@ -536,10 +535,8 @@ [:decl_type="abstract-int";value_type="u32"] [:decl_type="bool";value_type="abstract-float"] - expected: FAIL [:decl_type="bool";value_type="abstract-int"] - expected: FAIL [:decl_type="bool";value_type="bool"] @@ -580,7 +577,6 @@ [:decl_type="f32";value_type="u32"] [:decl_type="i32";value_type="abstract-float"] - expected: FAIL [:decl_type="i32";value_type="abstract-int"] @@ -595,7 +591,6 @@ [:decl_type="i32";value_type="u32"] [:decl_type="u32";value_type="abstract-float"] - expected: FAIL [:decl_type="u32";value_type="abstract-int"] @@ -611,12 +606,9 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,value_constructor:vector_elementwise:*] - implementation-status: backlog [:type="abstract-float";ele_type="abstract-float"] - expected: FAIL [:type="abstract-float";ele_type="abstract-int"] - expected: FAIL [:type="abstract-float";ele_type="bool"] @@ -637,10 +629,8 @@ [:type="abstract-float";ele_type="vec3f"] [:type="abstract-int";ele_type="abstract-float"] - expected: FAIL [:type="abstract-int";ele_type="abstract-int"] - expected: FAIL [:type="abstract-int";ele_type="bool"] @@ -772,12 +762,9 @@ [cts.https.html?q=webgpu:shader,validation,expression,call,builtin,value_constructor:vector_mixed:*] - implementation-status: backlog [:type="abstract-float";ele_type="abstract-float"] - expected: FAIL [:type="abstract-float";ele_type="abstract-int"] - expected: FAIL [:type="abstract-float";ele_type="bool"] @@ -790,10 +777,8 @@ [:type="abstract-float";ele_type="u32"] [:type="abstract-int";ele_type="abstract-float"] - expected: FAIL [:type="abstract-int";ele_type="abstract-int"] - expected: FAIL [:type="abstract-int";ele_type="bool"] @@ -923,8 +908,10 @@ [:type="abstract-int";ele_type="vec3f"] [:type="bool";ele_type="abstract-float"] + expected: FAIL [:type="bool";ele_type="abstract-int"] + expected: FAIL [:type="bool";ele_type="bool"] @@ -1007,6 +994,7 @@ [:type="f32";ele_type="vec3f"] [:type="i32";ele_type="abstract-float"] + expected: FAIL [:type="i32";ele_type="abstract-int"] @@ -1034,6 +1022,7 @@ [:type="i32";ele_type="vec3f"] [:type="u32";ele_type="abstract-float"] + expected: FAIL [:type="u32";ele_type="abstract-int"] @@ -1070,7 +1059,6 @@ expected: FAIL [:type="bool"] - expected: FAIL [:type="f16"] expected: diff --git a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/web_platform/copyToTexture/ImageBitmap/cts.https.html.ini b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/web_platform/copyToTexture/ImageBitmap/cts.https.html.ini index da1ae6907658..e808d83cb29d 100644 --- a/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/web_platform/copyToTexture/ImageBitmap/cts.https.html.ini +++ b/testing/web-platform/mozilla/meta/webgpu/cts/webgpu/web_platform/copyToTexture/ImageBitmap/cts.https.html.ini @@ -1683,11 +1683,8 @@ [:alpha="premultiply";orientation="none";colorSpaceConversion="default";srcFlipYInCopy=true;dstFormat="bgra8unorm";dstPremultiplied=false] expected: - if os == "win" and debug: [TIMEOUT, NOTRUN] - if os == "win" and not debug: [PASS, TIMEOUT, NOTRUN] - if os == "linux" and debug: [TIMEOUT, NOTRUN] - if os == "linux" and not debug: [PASS, TIMEOUT, NOTRUN] - if os == "mac" and debug: [TIMEOUT, NOTRUN] + if debug: [TIMEOUT, NOTRUN] + if not debug: [PASS, TIMEOUT, NOTRUN] [:alpha="premultiply";orientation="none";colorSpaceConversion="default";srcFlipYInCopy=true;dstFormat="bgra8unorm";dstPremultiplied=true] expected: diff --git a/third_party/rust/bytemuck/.cargo-checksum.json b/third_party/rust/bytemuck/.cargo-checksum.json new file mode 100644 index 000000000000..b10c25680994 --- /dev/null +++ b/third_party/rust/bytemuck/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.lock":"3cbd32854b2e798d7ba553ed70b875fd8ef1ba3b4d9ab29ee63c18d02ac4112e","Cargo.toml":"f13bc608a2ef6f83704f5b8da6b69f4eef9f64c24c9ceb03aab81b076703256a","LICENSE-APACHE":"870e20c217d15bcfcbe53d7c5867cd8fac44a4ca0b41fc1eb843557e16063eba","LICENSE-MIT":"0b2d108c9c686a74ac312990ee8377902756a2a081a7af3b0f9d68abf0a8f1a1","LICENSE-ZLIB":"682b4c81b85e83ce6cc6e1ace38fdd97aeb4de0e972bd2b44aa0916c54af8c96","README.md":"167493de1f1ad16d13c778494ae344cd71306622c89d19002eaf7f4185c1f728","changelog.md":"1ae85df3890cf5833836e1469953fe2f9f160cb50c63621471fd84d1fbfd69c5","rustfmt.toml":"f4c215534437936f924c937dbb1677f614761589300d6b389f3b518b3eb551b8","src/allocation.rs":"4d65e51a5d23963f60d11b8e95ae013de479c325088271bf1dba66c2f0afbb58","src/anybitpattern.rs":"172bcc22ba7b38a9a36e34d0aa5efc8716bba77569a3c142f16f8103046f1d45","src/checked.rs":"35f69278bf0106ce2855bff48ae3aeb125a54c3ee4afb123cbfe135d518e68c0","src/contiguous.rs":"bb340c0f20a06aa725bb625edcb493146ba64980fd1fd9e450654aa37384c564","src/internal.rs":"2b5b4fdc1a5d14462dd604ca1f4f23116f40639d7d8b4c1a7189ce276edd8272","src/lib.rs":"a6508d7ae80172dc88d9cba824387ab29783646c444b7be64b3f56f2bdb592e1","src/must.rs":"fed78b2a4dac5ef6758bab53a4f3bcabd8da071854bcc07dc70a255b327ec64c","src/no_uninit.rs":"4ab2f5ed29bff0b33630661154eb548f3e55581bfcf576a90397b7f8d5323201","src/offset_of.rs":"2afd190ef0462b30ade786fe813a91e7bf41cc2fa99a1d79002cbafab5964f37","src/pod.rs":"78c6a45ccefde5ebb715a8ac62e2cd5f45e808b1d0640f47a8a7427acd584a73","src/pod_in_option.rs":"73bbe1d69f32d909695ce26d131aa2d81eaa31e2b4532256ebfe1a6ba68675c1","src/transparent.rs":"5e555d6d83f5fe215ce2dbc949e87977aa92ab8d6f7a9be8479f63fdeda2359f","src/zeroable.rs":"80e24c607f240e6b18294e32c9a258d22da51318588396bbcf236c88eb6753f3","src/zeroable_in_option.rs":"f74799ac3eee50116ec63a0ae4d3e351e0ab7ac807d01b4b59027bf6a68d6de6","tests/array_tests.rs":"98ca7a0dcd93e65f70d4db19643e707cafae5a249561ab151998cedb89b2e036","tests/cast_slice_tests.rs":"1869416e833757f603765afbf48152a6d32a100316529eb494b48fca6c5a8899","tests/checked_tests.rs":"27965acf20e46482b09ee56aaa2536868821be651a3b95052f40e554ecde9917","tests/derive.rs":"93b5ab70ecdd726811af9dee1702e23e964b8ceac59f727889f6a2678ad90d65","tests/doc_tests.rs":"f20708319fde62d8957909d51ee976fce394ad0891ebc4bbcf336ab026a34092","tests/offset_of_tests.rs":"fb5f91e17f984050969f8b06f1de58b5c1e80802c5deb992d3188f5ec274690f","tests/std_tests.rs":"967d4fb4cae24a374633c9b68f1ff65f86ba4c8a0e980adfe69dcaf60a9049c2","tests/transparent.rs":"ecef6e0987e28121b480942e58ce4534f13fe35667bde7f5c6e04e590b02f6a3","tests/wrapper_forgets.rs":"c6330546f6aa696245625056e7323b3916e3fb1a9fbecefe9c9e62d3726812d9"},"package":"b6b1fc10dbac614ebc03540c9dbd60e83887fda27794998c6528f1782047d540"} \ No newline at end of file diff --git a/third_party/rust/bytemuck/Cargo.lock b/third_party/rust/bytemuck/Cargo.lock new file mode 100644 index 000000000000..1c10bf75baf9 --- /dev/null +++ b/third_party/rust/bytemuck/Cargo.lock @@ -0,0 +1,56 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bytemuck" +version = "1.22.0" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fa76293b4f7bb636ab88fd78228235b5248b4d05cc589aed610f954af5d7c7a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/third_party/rust/bytemuck/Cargo.toml b/third_party/rust/bytemuck/Cargo.toml new file mode 100644 index 000000000000..9d903348f5cc --- /dev/null +++ b/third_party/rust/bytemuck/Cargo.toml @@ -0,0 +1,141 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +name = "bytemuck" +version = "1.22.0" +authors = ["Lokathor "] +build = false +exclude = ["/pedantic.bat"] +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "A crate for mucking around with piles of bytes." +readme = "README.md" +keywords = [ + "transmute", + "bytes", + "casting", +] +categories = [ + "encoding", + "no-std", +] +license = "Zlib OR Apache-2.0 OR MIT" +repository = "https://github.com/Lokathor/bytemuck" + +[package.metadata.docs.rs] +features = [ + "nightly_docs", + "latest_stable_rust", + "extern_crate_alloc", + "extern_crate_std", +] + +[package.metadata.playground] +features = [ + "latest_stable_rust", + "extern_crate_alloc", + "extern_crate_std", +] + +[features] +aarch64_simd = [] +align_offset = [] +alloc_uninit = [] +avx512_simd = [] +const_zeroed = [] +derive = ["bytemuck_derive"] +extern_crate_alloc = [] +extern_crate_std = ["extern_crate_alloc"] +latest_stable_rust = [ + "aarch64_simd", + "avx512_simd", + "align_offset", + "alloc_uninit", + "const_zeroed", + "derive", + "min_const_generics", + "must_cast", + "must_cast_extra", + "pod_saturating", + "track_caller", + "transparentwrapper_extra", + "wasm_simd", + "zeroable_atomics", + "zeroable_maybe_uninit", +] +min_const_generics = [] +must_cast = [] +must_cast_extra = ["must_cast"] +nightly_docs = [] +nightly_float = [] +nightly_portable_simd = [] +nightly_stdsimd = [] +pod_saturating = [] +track_caller = [] +transparentwrapper_extra = [] +unsound_ptr_pod_impl = [] +wasm_simd = [] +zeroable_atomics = [] +zeroable_maybe_uninit = [] + +[lib] +name = "bytemuck" +path = "src/lib.rs" + +[[test]] +name = "array_tests" +path = "tests/array_tests.rs" + +[[test]] +name = "cast_slice_tests" +path = "tests/cast_slice_tests.rs" + +[[test]] +name = "checked_tests" +path = "tests/checked_tests.rs" + +[[test]] +name = "derive" +path = "tests/derive.rs" + +[[test]] +name = "doc_tests" +path = "tests/doc_tests.rs" + +[[test]] +name = "offset_of_tests" +path = "tests/offset_of_tests.rs" + +[[test]] +name = "std_tests" +path = "tests/std_tests.rs" + +[[test]] +name = "transparent" +path = "tests/transparent.rs" + +[[test]] +name = "wrapper_forgets" +path = "tests/wrapper_forgets.rs" + +[dependencies.bytemuck_derive] +version = "1.4.1" +optional = true + +[lints.rust.unexpected_cfgs] +level = "deny" +priority = 0 +check-cfg = ['cfg(target_arch, values("spirv"))'] diff --git a/third_party/rust/bytemuck/LICENSE-APACHE b/third_party/rust/bytemuck/LICENSE-APACHE new file mode 100644 index 000000000000..1d02268dc653 --- /dev/null +++ b/third_party/rust/bytemuck/LICENSE-APACHE @@ -0,0 +1,61 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/third_party/rust/bytemuck/LICENSE-MIT b/third_party/rust/bytemuck/LICENSE-MIT new file mode 100644 index 000000000000..0aa8816059dd --- /dev/null +++ b/third_party/rust/bytemuck/LICENSE-MIT @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2019 Daniel "Lokathor" Gee. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/third_party/rust/bytemuck/LICENSE-ZLIB b/third_party/rust/bytemuck/LICENSE-ZLIB new file mode 100644 index 000000000000..aa2dabe6345a --- /dev/null +++ b/third_party/rust/bytemuck/LICENSE-ZLIB @@ -0,0 +1,11 @@ +Copyright (c) 2019 Daniel "Lokathor" Gee. + +This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source distribution. diff --git a/third_party/rust/bytemuck/README.md b/third_party/rust/bytemuck/README.md new file mode 100644 index 000000000000..032316f7d782 --- /dev/null +++ b/third_party/rust/bytemuck/README.md @@ -0,0 +1,60 @@ +* **[Latest Docs.rs Here](https://docs.rs/bytemuck/)** + +[![License:Zlib](https://img.shields.io/badge/License-Zlib-brightgreen.svg)](https://opensource.org/licenses/Zlib) +![Minimum Rust Version](https://img.shields.io/badge/Min%20Rust-1.34-green.svg) +[![crates.io](https://img.shields.io/crates/v/bytemuck.svg)](https://crates.io/crates/bytemuck) + +# bytemuck + +A crate for mucking around with piles of bytes. + +This crate lets you safely perform "bit cast" operations between data types. +That's where you take a value and just reinterpret the bits as being some other +type of value, without changing the bits. + +* This is **not** like the [`as` keyword][keyword-as] +* This is **not** like the [`From` trait][from-trait] +* It is **most like** [`f32::to_bits`][f32-to_bits], just generalized to let you + convert between all sorts of data types. + +[keyword-as]: https://doc.rust-lang.org/nightly/std/keyword.as.html +[from-trait]: https://doc.rust-lang.org/nightly/core/convert/trait.From.html +[f32-to_bits]: https://doc.rust-lang.org/nightly/std/primitive.f32.html#method.to_bits + +### Here's the part you're more likely to care about: *you can do this with slices too!* + +When a slice is involved it's not a *direct* bitcast. Instead, the `cast_slice` +and `cast_slice_mut` functions will pull apart a slice's data and give you a new +slice that's the same span of memory just viewed as the new type. If the size of +the slice's element changes then the length of the slice you get back will be +changed accordingly. + +This lets you cast a slice of color values into a slice of `u8` and send it to +the GPU, or things like that. I'm sure there's other examples, but honestly this +crate is as popular as it is mostly because of Rust's 3D graphics community +wanting to cast slices of different types into byte slices for sending to the +GPU. Hi friends! Push those vertices, or whatever it is that you all do. + +## See Also + +While `bytemuck` is full of unsafe code, I've also started a "sibling crate" +called [bitfrob](https://docs.rs/bitfrob/latest/bitfrob/), which is where +operations that are 100% safe will be added. + +## Stability + +* The crate is 1.0 and I consider this it to be "basically done". New features + are usually being accepted when other people want to put in the work, but + myself I wanna move on to using `bytemuck` in bigger projects. +* The default build of the `bytemuck` crate will continue to work with `rustc-1.34` + for at least the rest of the `1.y.z` versions. +* Any other cargo features of the crate **are not** held to the same standard, and + may work only on the latest Stable or even only on latest Nightly. + +**Future Plans:** Once the [Safe Transmute Project][pg-st] completes and +stabilizes ("eventually") this crate will be updated to use that as the +underlying mechanism for transmutation bounds, and a 2.0 version of `bytemuck` +will be released. The hope is for the 1.0 to 2.0 transition to be as seamless as +possible, but the future is always uncertain. + +[pg-st]: https://rust-lang.github.io/rfcs/2835-project-safe-transmute.html diff --git a/third_party/rust/bytemuck/changelog.md b/third_party/rust/bytemuck/changelog.md new file mode 100644 index 000000000000..4848ae69e4c6 --- /dev/null +++ b/third_party/rust/bytemuck/changelog.md @@ -0,0 +1,363 @@ +# `bytemuck` changelog + +## 1.22 + +* Add the `pod_saturating` feature, which adds `Pod` impls for `Saturating` + when `T` is already `Pod`. +* A bump in the minimum `bytemuck_derive` dependency from 1.4.0 to 1.4.1 to + avoid a bug if you have a truly ancient `cargo.lock` file sitting around. +* Adds `Send` and `Sync` impls to `BoxBytes`. + +## 1.21 + +* Implement `Pod` and `Zeroable` for `core::arch::{x86, x86_64}::__m512`, `__m512d` and `__m512i` without nightly. + Requires Rust 1.72, and is gated through the `avx512_simd` cargo feature. +* Allow the use of `must_cast_mut` and `must_cast_slice_mut` in const contexts. + Requires Rust 1.83, and is gated through the `must_cast_extra` cargo feature. +* internal: introduced the `maybe_const_fn` macro that allows defining some function + to be const depending upon some `cfg` predicate. + +## 1.20 + +* New functions to allocate zeroed `Arc` and `Rc`. Requires Rust 1.82 +* `TransparentWrapper` impls for `core::cmp::Reverse` and `core::num::Saturating`. +* internal: Simplified the library's `fill_zeroes` calls to `write_bytes` + +## 1.19 + +* Adds the `#[track_caller]` attribute to functions which may panic. + +## 1.18 + +* Adds the `latest_stable_rust` cargo feature, which is a blanket feature that turns all other features on that are both sound and compatible with Stable rust. + +## 1.17.1 + +* Adds `#[repr(C)]` to the `union Transmute` type that's used internally + for most of the transmutations. + +## 1.17.0 + +* Makes the `must_cast` versions of the by-value and by-ref casts be `const`. + The mut ref cast is unaffected for now (mut references aren't yet stable in `const fn`). + This increases the MSRV of using that particular feature from 1.57 to 1.64. + +## 1.16.3 + +* Fully described in https://github.com/Lokathor/bytemuck/pull/256, This makes + casting slices to/from ZST elements more consistent between the crate's core + module and other modules. + +## 1.16.2 + +* Fixes potential UB where `BoxBytes` could attempt to free a dangling pointer + if the `Layout` is zero sized. This type was introduced in 1.14.1, so that + version and the others up to and including 1.16.1 are now yanked for safety. + +## 1.16.1 + +* **NOT SEMVER SUPPORTED:** Adds the `nightly_float` Cargo feature. This + activates the `f16` and `f128` nightly features, and then provides `Zeroable` + and `Pod` impls for those types. + +## 1.16.0 + +* Adds a `const_zeroed` feature (MSRV 1.75) which puts a `zeroed` fn at the crate root. + This is just like the `Zeroable::zeroed` method, but as a `const fn`. + +## 1.15.0 + +This primarily relaxes the bounds on a `From` impl. + +Previously: + +> `impl From> for BoxBytes` + +Now: + +> `impl From> for BoxBytes` + +All related functions and methods are similarly updated. + +We believe this to be backwards compatible with all previous uses, +and now `BoxBytes` can be converted to/from more types than before. + +## 1.14.3 + +* The new std simd nightly features are apparently arch-specific. + This adjusts the feature activation to be x86/ x86_64 only. + +## 1.14.2 + +* Changes the name of the Nightly feature activated by the crate's + `nightly_stdsimd` feature. This is needed as of (approximately) Nightly + 2024-02-06 and later, because the Nightly feature was changed. + +## 1.14.1 + +* docs clarifications. + +## 1.14 + +* `write_zeroes` and `fill_zeroes` functions: Writes (to one) or fills (a slice) + zero bytes to all bytes covered by the provided reference. If your type has + padding, this will even zero out the padding bytes. +* `align_offset` feature: causes pointer alignment checks to use the + `align_offset` pointer method rather than as-casting the pointer to `usize`. + This *may* improve codegen, if the compiler would have otherwise thought that + the pointer address escaped. No formal benchmarks have been done either way. +* `must_cast` feature: Adds `must_*` family of functions. These functions will + fail to compile if the cast requested can't be statically known to succeed. + The error messages can be kinda bad when this happens, but eliminating the + possibility of a runtime error might be worth it to you. + +## 1.13.1 + +* Remove the requirement for the *source* data type to be `AnyBitPattern` on + `pod_collect_to_vec`, allowing you to pod collect vecs of `char` into vecs of + `u32`, or whatever. + +## 1.13 + +* Now depends on `bytemuck_derive-1.4.0` +* Various small enhancements that would have been patch version updates, but + which have been rolled into this minor version update. + +## 1.12.4 + +* This has additional impls for existing traits and cleans up some internal code, + but there's no new functions so I guess it counts as just a patch release. + +## 1.12.3 + +* This bugfix makes the crate do stuff with `Arc` or not based on the + `target_has_atomic` config. Previously, some targets that have allocation but + not atomics were getting errors. This raises the MSRV of the + `extern_crate_alloc` feature to 1.60, but opt-in features are *not* considered + to be hard locked to 1.34 like the basic build of the crate is. + +## 1.12.2 + +* Fixes `try_pod_read_unaligned` bug that made it always fail unless the target + type was exactly pointer sized in which case UB *could* happen. The + `CheckedBitPattern::is_valid_bit_pattern` was being asked to check that a + *reference* to the `pod` value was a valid bit pattern, rather than the actual + bit pattern itself, and so the check could in some cases be illegally + bypassed. + +## 1.12.1 + +* Patch bumped the required `bytemuck_derive` version because of a regression in + how it handled `align(N)` attributes. + +## 1.12 + +* This minor version bump is caused by a version bump in our `bytemuck_derive` + dependency, which is in turn caused by a mixup in the minimum version of `syn` + that `bytemuck_derive` uses. See [Issue + 122](https://github.com/Lokathor/bytemuck/issues/122). There's not any + specific "new" API as you might normally expect from a minor version bump. +* [pali](https://github.com/pali6) fixed a problem with SPIR-V builds being + broken. The error handling functions were trying to be generic over `Display`, + which the error types normally support, except on SPIR-V targets (which run on + the GPU and don't have text formatting). + +## 1.11 + +* [WaffleLapkin](https://github.com/WaffleLapkin) added `wrap_box` and `peel_box` + to the `TransparentWrapperAlloc` trait. Default impls of these functions are + provided, and (as usual with the transparent trait stuff) you should not override + the default versions. + +## 1.10 + +* [TheEdward162](https://github.com/TheEdward162) added the `ZeroableInOption` + and `PodInOption` traits. These are for types that are `Zeroable` or `Pod` + *when in an option*, but not on their own. We provide impls for the various + "NonZeroINTEGER" types in `core`, and if you need to newtype a NonZero value + then you can impl these traits when you use `repr(transparent)`. + +## 1.9.1 + +* Bumped the minimum `bytemuck_derive` dependency version from `1.0` to `1.1`. + The fact that `bytemuck` and `bytemuck_derive` are separate crates at all is + an unfortunate technical limit of current Rust, woe and calamity. + +## 1.9.0 + +* [fu5ha](https://github.com/fu5ha) added the `NoUninit`, `AnyBitPattern`, and + `CheckedBitPattern` traits. This allows for a more fine-grained level of + detail in what casting operations are allowed for a type. Types that already + implement `Zeroable` and `Pod` will have a blanket impl for these new traits. + This is a "preview" of the direction that the crate will probably go in the + eventual 2.0 version. We're still waiting on [Project Safe + Transmute](https://github.com/rust-lang/project-safe-transmute) for an actual + 2.0 version of the crate, but until then please enjoy this preview. +* Also Fusha added better support for `union` types in the derive macros. I + still don't know how any of the proc-macro stuff works at all, so please + direct questions to her. + +## 1.8.0 + +* `try_pod_read_unaligned` and `pod_read_unaligned` let you go from `&[u8]` to + `T:Pod` without worrying about alignment. + +## 1.7.3 + +* Experimental support for the `portable_simd` language extension under the + `nightly_portable_simd` cargo feature. As the name implies, this is an + experimental crate feature and it's **not** part of the semver contract. All + it does is add the appropriate `Zeroable` and `Pod` impls. + +## 1.7.2 + +* Why does this repo keep being hit with publishing problems? What did I do to + deserve this curse, Ferris? This doesn't ever happen with tinyvec or fermium, + only bytemuck. + +## 1.7.1 + +* **Soundness Fix:** The wrap/peel methods for owned value conversion, added to + `TransparentWrapper` in 1.6, can cause a double-drop if used with types that + impl `Drop`. The fix was simply to add a `ManuallyDrop` layer around the value + before doing the `transmute_copy` that is used to wrap/peel. While this fix + could technically be backported to the 1.6 series, since 1.7 is semver + compatible anyway the 1.6 series has simply been yanked. + +## 1.7 + +* In response to [Unsafe Code Guidelines Issue + #286](https://github.com/rust-lang/unsafe-code-guidelines/issues/286), this + version of Bytemuck has a ***Soundness-Required Breaking Change***. This is + "allowed" under Rust's backwards-compatibility guidelines, but it's still + annoying of course so we're trying to keep the damage minimal. + * **The Reason:** It turns out that pointer values should not have been `Pod`. More + specifically, `ptr as usize` is *not* the same operation as calling + `transmute::<_, usize>(ptr)`. + * LLVM has yet to fully sort out their story, but until they do, transmuting + pointers can cause miscompilations. They may fix things up in the future, + but we're not gonna just wait and have broken code in the mean time. + * **The Fix:** The breaking change is that the `Pod` impls for `*const T`, + `*mut T`, and `Option` are now gated behind the + `unsound_ptr_pod_impl` feature, which is off by default. + * You are *strongly discouraged* from using this feature, but if a dependency + of yours doesn't work when you upgrade to 1.7 because it relied on pointer + casting, then you might wish to temporarily enable the feature just to get + that dependency to build. Enabled features are global across all users of a + given semver compatible version, so if you enable the feature in your own + crate, your dependency will also end up getting the feature too, and then + it'll be able to compile. + * Please move away from using this feature as soon as you can. Consider it to + *already* be deprecated. + * [PR 65](https://github.com/Lokathor/bytemuck/pull/65) + +## 1.6.3 + +* Small goof with an errant `;`, so [PR 69](https://github.com/Lokathor/bytemuck/pull/69) + *actually* got things working on SPIR-V. + +## 1.6.2 + +cargo upload goof! ignore this one. + +## 1.6.1 + +* [DJMcNab](https://github.com/DJMcNab) did a fix so that the crate can build for SPIR-V + [PR 67](https://github.com/Lokathor/bytemuck/pull/67) + +## 1.6 + +* The `TransparentWrapper` trait now has more methods. More ways to wrap, and + now you can "peel" too! Note that we don't call it "unwrap" because that name + is too strongly associated with the Option/Result methods. + Thanks to [LU15W1R7H](https://github.com/LU15W1R7H) for doing + [PR 58](https://github.com/Lokathor/bytemuck/pull/58) +* Min Const Generics! Now there's Pod and Zeroable for arrays of any size when + you turn on the `min_const_generics` crate feature. + [zakarumych](https://github.com/zakarumych) got the work started in + [PR 59](https://github.com/Lokathor/bytemuck/pull/59), + and [chorman0773](https://github.com/chorman0773) finished off the task in + [PR 63](https://github.com/Lokathor/bytemuck/pull/63) + +## 1.5.1 + +* Fix `bytes_of` failing on zero sized types. + [PR 53](https://github.com/Lokathor/bytemuck/pull/53) + +## 1.5 + +* Added `pod_collect_to_vec`, which will gather a slice into a vec, +allowing you to change the pod type while also safely ignoring alignment. +[PR 50](https://github.com/Lokathor/bytemuck/pull/50) + +## 1.4.2 + +* [Kimundi](https://github.com/Kimundi) fixed an issue that could make `try_zeroed_box` +stack overflow for large values at low optimization levels. +[PR 43](https://github.com/Lokathor/bytemuck/pull/43) + +## 1.4.1 + +* [thomcc](https://github.com/thomcc) fixed up the CI and patched over a soundness hole in `offset_of!`. +[PR 38](https://github.com/Lokathor/bytemuck/pull/38) + +## 1.4 + +* [icewind1991](https://github.com/icewind1991) has contributed the proc-macros + for deriving impls of `Pod`, `TransparentWrapper`, `Zeroable`!! Everyone has + been waiting for this one folks! It's a big deal. Just enable the `derive` + cargo feature and then you'll be able to derive the traits on your types. It + generates all the appropriate tests for you. +* The `zeroable_maybe_uninit` feature now adds a `Zeroable` impl to the + `MaybeUninit` type. This is only behind a feature flag because `MaybeUninit` + didn't exist back in `1.34.0` (the minimum rust version of `bytemuck`). + +## 1.3.1 + +* The entire crate is now available under the `Apache-2.0 OR MIT` license as + well as the previous `Zlib` license + [#24](https://github.com/Lokathor/bytemuck/pull/24). +* [HeroicKatora](https://github.com/HeroicKatora) added the + `try_zeroed_slice_box` function + [#10](https://github.com/Lokathor/bytemuck/pull/17). `zeroed_slice_box` is + also available. +* The `offset_of!` macro now supports a 2-arg version. For types that impl + Default, it'll just make an instance using `default` and then call over to the + 3-arg version. +* The `PodCastError` type now supports `Hash` and `Display`. Also if you enable + the `extern_crate_std` feature then it will support `std::error::Error`. +* We now provide a `TransparentWrapper` impl for `core::num::Wrapper`. +* The error type of `try_from_bytes` and `try_from_bytes_mut` when the input + isn't aligned has been corrected from being `AlignmentMismatch` (intended for + allocation casting only) to `TargetAlignmentGreaterAndInputNotAligned`. + +## 1.3.0 + +* Had a bug because the CI was messed up! It wasn't soundness related, because + it prevented the crate from building entirely if the `extern_crate_alloc` + feature was used. Still, this is yanked, sorry. + +## 1.2.0 + +* [thomcc](https://github.com/thomcc) added many things: + * A fully sound `offset_of!` macro + [#10](https://github.com/Lokathor/bytemuck/pull/10) + * A `Contiguous` trait for when you've got enums with declared values + all in a row [#12](https://github.com/Lokathor/bytemuck/pull/12) + * A `TransparentWrapper` marker trait for when you want to more clearly + enable adding and removing a wrapper struct to its inner value + [#15](https://github.com/Lokathor/bytemuck/pull/15) + * Now MIRI is run on CI in every single push! + [#16](https://github.com/Lokathor/bytemuck/pull/16) + +## 1.1.0 + +* [SimonSapin](https://github.com/SimonSapin) added `from_bytes`, + `from_bytes_mut`, `try_from_bytes`, and `try_from_bytes_mut` ([PR + Link](https://github.com/Lokathor/bytemuck/pull/8)) + +## 1.0.1 + +* Changed to the [zlib](https://opensource.org/licenses/Zlib) license. +* Added much more proper documentation. +* Reduced the minimum Rust version to 1.34 diff --git a/third_party/rust/bytemuck/rustfmt.toml b/third_party/rust/bytemuck/rustfmt.toml new file mode 100644 index 000000000000..a572164a0246 --- /dev/null +++ b/third_party/rust/bytemuck/rustfmt.toml @@ -0,0 +1,16 @@ +# Based on +# https://github.com/rust-lang/rustfmt/blob/rustfmt-1.4.19/Configurations.md + +# Stable +edition = "2018" +fn_args_layout = "Compressed" +max_width = 80 +tab_spaces = 2 +use_field_init_shorthand = true +use_try_shorthand = true +use_small_heuristics = "Max" + +# Unstable +format_code_in_doc_comments = true +imports_granularity = "Crate" +wrap_comments = true diff --git a/third_party/rust/bytemuck/src/allocation.rs b/third_party/rust/bytemuck/src/allocation.rs new file mode 100644 index 000000000000..458dedb61270 --- /dev/null +++ b/third_party/rust/bytemuck/src/allocation.rs @@ -0,0 +1,980 @@ +#![cfg(feature = "extern_crate_alloc")] +#![allow(clippy::duplicated_attributes)] + +//! Stuff to boost things in the `alloc` crate. +//! +//! * You must enable the `extern_crate_alloc` feature of `bytemuck` or you will +//! not be able to use this module! This is generally done by adding the +//! feature to the dependency in Cargo.toml like so: +//! +//! `bytemuck = { version = "VERSION_YOU_ARE_USING", features = +//! ["extern_crate_alloc"]}` + +use super::*; +#[cfg(target_has_atomic = "ptr")] +use alloc::sync::Arc; +use alloc::{ + alloc::{alloc_zeroed, Layout}, + boxed::Box, + rc::Rc, + vec, + vec::Vec, +}; +use core::{ + mem::{size_of_val, ManuallyDrop}, + ops::{Deref, DerefMut}, +}; + +/// As [`try_cast_box`], but unwraps for you. +#[inline] +pub fn cast_box(input: Box) -> Box { + try_cast_box(input).map_err(|(e, _v)| e).unwrap() +} + +/// Attempts to cast the content type of a [`Box`]. +/// +/// On failure you get back an error along with the starting `Box`. +/// +/// ## Failure +/// +/// * The start and end content type of the `Box` must have the exact same +/// alignment. +/// * The start and end size of the `Box` must have the exact same size. +#[inline] +pub fn try_cast_box( + input: Box, +) -> Result, (PodCastError, Box)> { + if align_of::() != align_of::() { + Err((PodCastError::AlignmentMismatch, input)) + } else if size_of::() != size_of::() { + Err((PodCastError::SizeMismatch, input)) + } else { + // Note(Lokathor): This is much simpler than with the Vec casting! + let ptr: *mut B = Box::into_raw(input) as *mut B; + Ok(unsafe { Box::from_raw(ptr) }) + } +} + +/// Allocates a `Box` with all of the contents being zeroed out. +/// +/// This uses the global allocator to create a zeroed allocation and _then_ +/// turns it into a Box. In other words, it's 100% assured that the zeroed data +/// won't be put temporarily on the stack. You can make a box of any size +/// without fear of a stack overflow. +/// +/// ## Failure +/// +/// This fails if the allocation fails. +#[inline] +pub fn try_zeroed_box() -> Result, ()> { + if size_of::() == 0 { + // This will not allocate but simply create an arbitrary non-null + // aligned pointer, valid for Box for a zero-sized pointee. + let ptr = core::ptr::NonNull::dangling().as_ptr(); + return Ok(unsafe { Box::from_raw(ptr) }); + } + let layout = Layout::new::(); + let ptr = unsafe { alloc_zeroed(layout) }; + if ptr.is_null() { + // we don't know what the error is because `alloc_zeroed` is a dumb API + Err(()) + } else { + Ok(unsafe { Box::::from_raw(ptr as *mut T) }) + } +} + +/// As [`try_zeroed_box`], but unwraps for you. +#[inline] +pub fn zeroed_box() -> Box { + try_zeroed_box().unwrap() +} + +/// Allocates a `Vec` of length and capacity exactly equal to `length` and +/// all elements zeroed. +/// +/// ## Failure +/// +/// This fails if the allocation fails, or if a layout cannot be calculated for +/// the allocation. +pub fn try_zeroed_vec(length: usize) -> Result, ()> { + if length == 0 { + Ok(Vec::new()) + } else { + let boxed_slice = try_zeroed_slice_box(length)?; + Ok(boxed_slice.into_vec()) + } +} + +/// As [`try_zeroed_vec`] but unwraps for you +pub fn zeroed_vec(length: usize) -> Vec { + try_zeroed_vec(length).unwrap() +} + +/// Allocates a `Box<[T]>` with all contents being zeroed out. +/// +/// This uses the global allocator to create a zeroed allocation and _then_ +/// turns it into a Box. In other words, it's 100% assured that the zeroed data +/// won't be put temporarily on the stack. You can make a box of any size +/// without fear of a stack overflow. +/// +/// ## Failure +/// +/// This fails if the allocation fails, or if a layout cannot be calculated for +/// the allocation. +#[inline] +pub fn try_zeroed_slice_box( + length: usize, +) -> Result, ()> { + if size_of::() == 0 || length == 0 { + // This will not allocate but simply create an arbitrary non-null aligned + // slice pointer, valid for Box for a zero-sized pointee. + let ptr = core::ptr::NonNull::dangling().as_ptr(); + let slice_ptr = core::ptr::slice_from_raw_parts_mut(ptr, length); + return Ok(unsafe { Box::from_raw(slice_ptr) }); + } + let layout = core::alloc::Layout::array::(length).map_err(|_| ())?; + let ptr = unsafe { alloc_zeroed(layout) }; + if ptr.is_null() { + // we don't know what the error is because `alloc_zeroed` is a dumb API + Err(()) + } else { + let slice = + unsafe { core::slice::from_raw_parts_mut(ptr as *mut T, length) }; + Ok(unsafe { Box::<[T]>::from_raw(slice) }) + } +} + +/// As [`try_zeroed_slice_box`], but unwraps for you. +pub fn zeroed_slice_box(length: usize) -> Box<[T]> { + try_zeroed_slice_box(length).unwrap() +} + +/// Allocates a `Arc` with all contents being zeroed out. +#[cfg(all(feature = "alloc_uninit", target_has_atomic = "ptr"))] +pub fn zeroed_arc() -> Arc { + let mut arc = Arc::new_uninit(); + crate::write_zeroes(Arc::get_mut(&mut arc).unwrap()); // unwrap never fails for a newly allocated Arc + unsafe { arc.assume_init() } +} + +/// Allocates a `Arc<[T]>` with all contents being zeroed out. +#[cfg(all(feature = "alloc_uninit", target_has_atomic = "ptr"))] +pub fn zeroed_arc_slice(length: usize) -> Arc<[T]> { + let mut arc = Arc::new_uninit_slice(length); + crate::fill_zeroes(Arc::get_mut(&mut arc).unwrap()); // unwrap never fails for a newly allocated Arc + unsafe { arc.assume_init() } +} + +/// Allocates a `Rc` with all contents being zeroed out. +#[cfg(feature = "alloc_uninit")] +pub fn zeroed_rc() -> Rc { + let mut rc = Rc::new_uninit(); + crate::write_zeroes(Rc::get_mut(&mut rc).unwrap()); // unwrap never fails for a newly allocated Rc + unsafe { rc.assume_init() } +} + +/// Allocates a `Rc<[T]>` with all contents being zeroed out. +#[cfg(feature = "alloc_uninit")] +pub fn zeroed_rc_slice(length: usize) -> Rc<[T]> { + let mut rc = Rc::new_uninit_slice(length); + crate::fill_zeroes(Rc::get_mut(&mut rc).unwrap()); // unwrap never fails for a newly allocated Rc + unsafe { rc.assume_init() } +} + +/// As [`try_cast_slice_box`], but unwraps for you. +#[inline] +pub fn cast_slice_box( + input: Box<[A]>, +) -> Box<[B]> { + try_cast_slice_box(input).map_err(|(e, _v)| e).unwrap() +} + +/// Attempts to cast the content type of a `Box<[T]>`. +/// +/// On failure you get back an error along with the starting `Box<[T]>`. +/// +/// ## Failure +/// +/// * The start and end content type of the `Box<[T]>` must have the exact same +/// alignment. +/// * The start and end content size in bytes of the `Box<[T]>` must be the +/// exact same. +#[inline] +pub fn try_cast_slice_box( + input: Box<[A]>, +) -> Result, (PodCastError, Box<[A]>)> { + if align_of::() != align_of::() { + Err((PodCastError::AlignmentMismatch, input)) + } else if size_of::() != size_of::() { + let input_bytes = size_of_val::<[A]>(&*input); + if (size_of::() == 0 && input_bytes != 0) + || (size_of::() != 0 && input_bytes % size_of::() != 0) + { + // If the size in bytes of the underlying buffer does not match an exact + // multiple of the size of B, we cannot cast between them. + Err((PodCastError::OutputSliceWouldHaveSlop, input)) + } else { + // Because the size is an exact multiple, we can now change the length + // of the slice and recreate the Box + // NOTE: This is a valid operation because according to the docs of + // std::alloc::GlobalAlloc::dealloc(), the Layout that was used to alloc + // the block must be the same Layout that is used to dealloc the block. + // Luckily, Layout only stores two things, the alignment, and the size in + // bytes. So as long as both of those stay the same, the Layout will + // remain a valid input to dealloc. + let length = + if size_of::() != 0 { input_bytes / size_of::() } else { 0 }; + let box_ptr: *mut A = Box::into_raw(input) as *mut A; + let ptr: *mut [B] = + unsafe { core::slice::from_raw_parts_mut(box_ptr as *mut B, length) }; + Ok(unsafe { Box::<[B]>::from_raw(ptr) }) + } + } else { + let box_ptr: *mut [A] = Box::into_raw(input); + let ptr: *mut [B] = box_ptr as *mut [B]; + Ok(unsafe { Box::<[B]>::from_raw(ptr) }) + } +} + +/// As [`try_cast_vec`], but unwraps for you. +#[inline] +pub fn cast_vec(input: Vec) -> Vec { + try_cast_vec(input).map_err(|(e, _v)| e).unwrap() +} + +/// Attempts to cast the content type of a [`Vec`]. +/// +/// On failure you get back an error along with the starting `Vec`. +/// +/// ## Failure +/// +/// * The start and end content type of the `Vec` must have the exact same +/// alignment. +/// * The start and end content size in bytes of the `Vec` must be the exact +/// same. +/// * The start and end capacity in bytes of the `Vec` must be the exact same. +#[inline] +pub fn try_cast_vec( + input: Vec, +) -> Result, (PodCastError, Vec)> { + if align_of::() != align_of::() { + Err((PodCastError::AlignmentMismatch, input)) + } else if size_of::() != size_of::() { + let input_size = size_of_val::<[A]>(&*input); + let input_capacity = input.capacity() * size_of::(); + if (size_of::() == 0 && input_capacity != 0) + || (size_of::() != 0 + && (input_size % size_of::() != 0 + || input_capacity % size_of::() != 0)) + { + // If the size in bytes of the underlying buffer does not match an exact + // multiple of the size of B, we cannot cast between them. + // Note that we have to pay special attention to make sure that both + // length and capacity are valid under B, as we do not want to + // change which bytes are considered part of the initialized slice + // of the Vec + Err((PodCastError::OutputSliceWouldHaveSlop, input)) + } else { + // Because the size is an exact multiple, we can now change the length and + // capacity and recreate the Vec + // NOTE: This is a valid operation because according to the docs of + // std::alloc::GlobalAlloc::dealloc(), the Layout that was used to alloc + // the block must be the same Layout that is used to dealloc the block. + // Luckily, Layout only stores two things, the alignment, and the size in + // bytes. So as long as both of those stay the same, the Layout will + // remain a valid input to dealloc. + + // Note(Lokathor): First we record the length and capacity, which don't + // have any secret provenance metadata. + let length: usize = + if size_of::() != 0 { input_size / size_of::() } else { 0 }; + let capacity: usize = + if size_of::() != 0 { input_capacity / size_of::() } else { 0 }; + // Note(Lokathor): Next we "pre-forget" the old Vec by wrapping with + // ManuallyDrop, because if we used `core::mem::forget` after taking the + // pointer then that would invalidate our pointer. In nightly there's a + // "into raw parts" method, which we can switch this too eventually. + let mut manual_drop_vec = ManuallyDrop::new(input); + let vec_ptr: *mut A = manual_drop_vec.as_mut_ptr(); + let ptr: *mut B = vec_ptr as *mut B; + Ok(unsafe { Vec::from_raw_parts(ptr, length, capacity) }) + } + } else { + // Note(Lokathor): First we record the length and capacity, which don't have + // any secret provenance metadata. + let length: usize = input.len(); + let capacity: usize = input.capacity(); + // Note(Lokathor): Next we "pre-forget" the old Vec by wrapping with + // ManuallyDrop, because if we used `core::mem::forget` after taking the + // pointer then that would invalidate our pointer. In nightly there's a + // "into raw parts" method, which we can switch this too eventually. + let mut manual_drop_vec = ManuallyDrop::new(input); + let vec_ptr: *mut A = manual_drop_vec.as_mut_ptr(); + let ptr: *mut B = vec_ptr as *mut B; + Ok(unsafe { Vec::from_raw_parts(ptr, length, capacity) }) + } +} + +/// This "collects" a slice of pod data into a vec of a different pod type. +/// +/// Unlike with [`cast_slice`] and [`cast_slice_mut`], this will always work. +/// +/// The output vec will be of a minimal size/capacity to hold the slice given. +/// +/// ```rust +/// # use bytemuck::*; +/// let halfwords: [u16; 4] = [5, 6, 7, 8]; +/// let vec_of_words: Vec = pod_collect_to_vec(&halfwords); +/// if cfg!(target_endian = "little") { +/// assert_eq!(&vec_of_words[..], &[0x0006_0005, 0x0008_0007][..]) +/// } else { +/// assert_eq!(&vec_of_words[..], &[0x0005_0006, 0x0007_0008][..]) +/// } +/// ``` +pub fn pod_collect_to_vec( + src: &[A], +) -> Vec { + let src_size = core::mem::size_of_val(src); + // Note(Lokathor): dst_count is rounded up so that the dest will always be at + // least as many bytes as the src. + let dst_count = src_size / size_of::() + + if src_size % size_of::() != 0 { 1 } else { 0 }; + let mut dst = vec![B::zeroed(); dst_count]; + + let src_bytes: &[u8] = cast_slice(src); + let dst_bytes: &mut [u8] = cast_slice_mut(&mut dst[..]); + dst_bytes[..src_size].copy_from_slice(src_bytes); + dst +} + +/// As [`try_cast_rc`], but unwraps for you. +#[inline] +pub fn cast_rc( + input: Rc, +) -> Rc { + try_cast_rc(input).map_err(|(e, _v)| e).unwrap() +} + +/// Attempts to cast the content type of a [`Rc`]. +/// +/// On failure you get back an error along with the starting `Rc`. +/// +/// The bounds on this function are the same as [`cast_mut`], because a user +/// could call `Rc::get_unchecked_mut` on the output, which could be observable +/// in the input. +/// +/// ## Failure +/// +/// * The start and end content type of the `Rc` must have the exact same +/// alignment. +/// * The start and end size of the `Rc` must have the exact same size. +#[inline] +pub fn try_cast_rc( + input: Rc, +) -> Result, (PodCastError, Rc)> { + if align_of::() != align_of::() { + Err((PodCastError::AlignmentMismatch, input)) + } else if size_of::() != size_of::() { + Err((PodCastError::SizeMismatch, input)) + } else { + // Safety: Rc::from_raw requires size and alignment match, which is met. + let ptr: *const B = Rc::into_raw(input) as *const B; + Ok(unsafe { Rc::from_raw(ptr) }) + } +} + +/// As [`try_cast_arc`], but unwraps for you. +#[inline] +#[cfg(target_has_atomic = "ptr")] +pub fn cast_arc( + input: Arc, +) -> Arc { + try_cast_arc(input).map_err(|(e, _v)| e).unwrap() +} + +/// Attempts to cast the content type of a [`Arc`]. +/// +/// On failure you get back an error along with the starting `Arc`. +/// +/// The bounds on this function are the same as [`cast_mut`], because a user +/// could call `Rc::get_unchecked_mut` on the output, which could be observable +/// in the input. +/// +/// ## Failure +/// +/// * The start and end content type of the `Arc` must have the exact same +/// alignment. +/// * The start and end size of the `Arc` must have the exact same size. +#[inline] +#[cfg(target_has_atomic = "ptr")] +pub fn try_cast_arc< + A: NoUninit + AnyBitPattern, + B: NoUninit + AnyBitPattern, +>( + input: Arc, +) -> Result, (PodCastError, Arc)> { + if align_of::() != align_of::() { + Err((PodCastError::AlignmentMismatch, input)) + } else if size_of::() != size_of::() { + Err((PodCastError::SizeMismatch, input)) + } else { + // Safety: Arc::from_raw requires size and alignment match, which is met. + let ptr: *const B = Arc::into_raw(input) as *const B; + Ok(unsafe { Arc::from_raw(ptr) }) + } +} + +/// As [`try_cast_slice_rc`], but unwraps for you. +#[inline] +pub fn cast_slice_rc< + A: NoUninit + AnyBitPattern, + B: NoUninit + AnyBitPattern, +>( + input: Rc<[A]>, +) -> Rc<[B]> { + try_cast_slice_rc(input).map_err(|(e, _v)| e).unwrap() +} + +/// Attempts to cast the content type of a `Rc<[T]>`. +/// +/// On failure you get back an error along with the starting `Rc<[T]>`. +/// +/// The bounds on this function are the same as [`cast_mut`], because a user +/// could call `Rc::get_unchecked_mut` on the output, which could be observable +/// in the input. +/// +/// ## Failure +/// +/// * The start and end content type of the `Rc<[T]>` must have the exact same +/// alignment. +/// * The start and end content size in bytes of the `Rc<[T]>` must be the exact +/// same. +#[inline] +pub fn try_cast_slice_rc< + A: NoUninit + AnyBitPattern, + B: NoUninit + AnyBitPattern, +>( + input: Rc<[A]>, +) -> Result, (PodCastError, Rc<[A]>)> { + if align_of::() != align_of::() { + Err((PodCastError::AlignmentMismatch, input)) + } else if size_of::() != size_of::() { + let input_bytes = size_of_val::<[A]>(&*input); + if (size_of::() == 0 && input_bytes != 0) + || (size_of::() != 0 && input_bytes % size_of::() != 0) + { + // If the size in bytes of the underlying buffer does not match an exact + // multiple of the size of B, we cannot cast between them. + Err((PodCastError::OutputSliceWouldHaveSlop, input)) + } else { + // Because the size is an exact multiple, we can now change the length + // of the slice and recreate the Rc + // NOTE: This is a valid operation because according to the docs of + // std::rc::Rc::from_raw(), the type U that was in the original Rc + // acquired from Rc::into_raw() must have the same size alignment and + // size of the type T in the new Rc. So as long as both the size + // and alignment stay the same, the Rc will remain a valid Rc. + let length = + if size_of::() != 0 { input_bytes / size_of::() } else { 0 }; + let rc_ptr: *const A = Rc::into_raw(input) as *const A; + // Must use ptr::slice_from_raw_parts, because we cannot make an + // intermediate const reference, because it has mutable provenance, + // nor an intermediate mutable reference, because it could be aliased. + let ptr = core::ptr::slice_from_raw_parts(rc_ptr as *const B, length); + Ok(unsafe { Rc::<[B]>::from_raw(ptr) }) + } + } else { + let rc_ptr: *const [A] = Rc::into_raw(input); + let ptr: *const [B] = rc_ptr as *const [B]; + Ok(unsafe { Rc::<[B]>::from_raw(ptr) }) + } +} + +/// As [`try_cast_slice_arc`], but unwraps for you. +#[inline] +#[cfg(target_has_atomic = "ptr")] +pub fn cast_slice_arc< + A: NoUninit + AnyBitPattern, + B: NoUninit + AnyBitPattern, +>( + input: Arc<[A]>, +) -> Arc<[B]> { + try_cast_slice_arc(input).map_err(|(e, _v)| e).unwrap() +} + +/// Attempts to cast the content type of a `Arc<[T]>`. +/// +/// On failure you get back an error along with the starting `Arc<[T]>`. +/// +/// The bounds on this function are the same as [`cast_mut`], because a user +/// could call `Rc::get_unchecked_mut` on the output, which could be observable +/// in the input. +/// +/// ## Failure +/// +/// * The start and end content type of the `Arc<[T]>` must have the exact same +/// alignment. +/// * The start and end content size in bytes of the `Arc<[T]>` must be the +/// exact same. +#[inline] +#[cfg(target_has_atomic = "ptr")] +pub fn try_cast_slice_arc< + A: NoUninit + AnyBitPattern, + B: NoUninit + AnyBitPattern, +>( + input: Arc<[A]>, +) -> Result, (PodCastError, Arc<[A]>)> { + if align_of::() != align_of::() { + Err((PodCastError::AlignmentMismatch, input)) + } else if size_of::() != size_of::() { + let input_bytes = size_of_val::<[A]>(&*input); + if (size_of::() == 0 && input_bytes != 0) + || (size_of::() != 0 && input_bytes % size_of::() != 0) + { + // If the size in bytes of the underlying buffer does not match an exact + // multiple of the size of B, we cannot cast between them. + Err((PodCastError::OutputSliceWouldHaveSlop, input)) + } else { + // Because the size is an exact multiple, we can now change the length + // of the slice and recreate the Arc + // NOTE: This is a valid operation because according to the docs of + // std::sync::Arc::from_raw(), the type U that was in the original Arc + // acquired from Arc::into_raw() must have the same size alignment and + // size of the type T in the new Arc. So as long as both the size + // and alignment stay the same, the Arc will remain a valid Arc. + let length = + if size_of::() != 0 { input_bytes / size_of::() } else { 0 }; + let arc_ptr: *const A = Arc::into_raw(input) as *const A; + // Must use ptr::slice_from_raw_parts, because we cannot make an + // intermediate const reference, because it has mutable provenance, + // nor an intermediate mutable reference, because it could be aliased. + let ptr = core::ptr::slice_from_raw_parts(arc_ptr as *const B, length); + Ok(unsafe { Arc::<[B]>::from_raw(ptr) }) + } + } else { + let arc_ptr: *const [A] = Arc::into_raw(input); + let ptr: *const [B] = arc_ptr as *const [B]; + Ok(unsafe { Arc::<[B]>::from_raw(ptr) }) + } +} + +/// An extension trait for `TransparentWrapper` and alloc types. +pub trait TransparentWrapperAlloc: + TransparentWrapper +{ + /// Convert a vec of the inner type into a vec of the wrapper type. + fn wrap_vec(s: Vec) -> Vec + where + Self: Sized, + Inner: Sized, + { + let mut s = ManuallyDrop::new(s); + + let length = s.len(); + let capacity = s.capacity(); + let ptr = s.as_mut_ptr(); + + unsafe { + // SAFETY: + // * ptr comes from Vec (and will not be double-dropped) + // * the two types have the identical representation + // * the len and capacity fields are valid + Vec::from_raw_parts(ptr as *mut Self, length, capacity) + } + } + + /// Convert a box to the inner type into a box to the wrapper + /// type. + #[inline] + fn wrap_box(s: Box) -> Box { + // The unsafe contract requires that these two have + // identical representations, and thus identical pointer metadata. + // Assert that Self and Inner have the same pointer size, + // which is the best we can do to assert their metadata is the same type + // on stable. + assert!(size_of::<*mut Inner>() == size_of::<*mut Self>()); + + unsafe { + // A pointer cast doesn't work here because rustc can't tell that + // the vtables match (because of the `?Sized` restriction relaxation). + // A `transmute` doesn't work because the sizes are unspecified. + // + // SAFETY: + // * The unsafe contract requires that pointers to Inner and Self have + // identical representations + // * Box is guaranteed to have representation identical to a (non-null) + // pointer + // * The pointer comes from a box (and thus satisfies all safety + // requirements of Box) + let inner_ptr: *mut Inner = Box::into_raw(s); + let wrapper_ptr: *mut Self = transmute!(inner_ptr); + Box::from_raw(wrapper_ptr) + } + } + + /// Convert an [`Rc`] to the inner type into an `Rc` to the wrapper type. + #[inline] + fn wrap_rc(s: Rc) -> Rc { + // The unsafe contract requires that these two have + // identical representations, and thus identical pointer metadata. + // Assert that Self and Inner have the same pointer size, + // which is the best we can do to assert their metadata is the same type + // on stable. + assert!(size_of::<*mut Inner>() == size_of::<*mut Self>()); + + unsafe { + // A pointer cast doesn't work here because rustc can't tell that + // the vtables match (because of the `?Sized` restriction relaxation). + // A `transmute` doesn't work because the layout of Rc is unspecified. + // + // SAFETY: + // * The unsafe contract requires that pointers to Inner and Self have + // identical representations, and that the size and alignment of Inner + // and Self are the same, which meets the safety requirements of + // Rc::from_raw + let inner_ptr: *const Inner = Rc::into_raw(s); + let wrapper_ptr: *const Self = transmute!(inner_ptr); + Rc::from_raw(wrapper_ptr) + } + } + + /// Convert an [`Arc`] to the inner type into an `Arc` to the wrapper type. + #[inline] + #[cfg(target_has_atomic = "ptr")] + fn wrap_arc(s: Arc) -> Arc { + // The unsafe contract requires that these two have + // identical representations, and thus identical pointer metadata. + // Assert that Self and Inner have the same pointer size, + // which is the best we can do to assert their metadata is the same type + // on stable. + assert!(size_of::<*mut Inner>() == size_of::<*mut Self>()); + + unsafe { + // A pointer cast doesn't work here because rustc can't tell that + // the vtables match (because of the `?Sized` restriction relaxation). + // A `transmute` doesn't work because the layout of Arc is unspecified. + // + // SAFETY: + // * The unsafe contract requires that pointers to Inner and Self have + // identical representations, and that the size and alignment of Inner + // and Self are the same, which meets the safety requirements of + // Arc::from_raw + let inner_ptr: *const Inner = Arc::into_raw(s); + let wrapper_ptr: *const Self = transmute!(inner_ptr); + Arc::from_raw(wrapper_ptr) + } + } + + /// Convert a vec of the wrapper type into a vec of the inner type. + fn peel_vec(s: Vec) -> Vec + where + Self: Sized, + Inner: Sized, + { + let mut s = ManuallyDrop::new(s); + + let length = s.len(); + let capacity = s.capacity(); + let ptr = s.as_mut_ptr(); + + unsafe { + // SAFETY: + // * ptr comes from Vec (and will not be double-dropped) + // * the two types have the identical representation + // * the len and capacity fields are valid + Vec::from_raw_parts(ptr as *mut Inner, length, capacity) + } + } + + /// Convert a box to the wrapper type into a box to the inner + /// type. + #[inline] + fn peel_box(s: Box) -> Box { + // The unsafe contract requires that these two have + // identical representations, and thus identical pointer metadata. + // Assert that Self and Inner have the same pointer size, + // which is the best we can do to assert their metadata is the same type + // on stable. + assert!(size_of::<*mut Inner>() == size_of::<*mut Self>()); + + unsafe { + // A pointer cast doesn't work here because rustc can't tell that + // the vtables match (because of the `?Sized` restriction relaxation). + // A `transmute` doesn't work because the sizes are unspecified. + // + // SAFETY: + // * The unsafe contract requires that pointers to Inner and Self have + // identical representations + // * Box is guaranteed to have representation identical to a (non-null) + // pointer + // * The pointer comes from a box (and thus satisfies all safety + // requirements of Box) + let wrapper_ptr: *mut Self = Box::into_raw(s); + let inner_ptr: *mut Inner = transmute!(wrapper_ptr); + Box::from_raw(inner_ptr) + } + } + + /// Convert an [`Rc`] to the wrapper type into an `Rc` to the inner type. + #[inline] + fn peel_rc(s: Rc) -> Rc { + // The unsafe contract requires that these two have + // identical representations, and thus identical pointer metadata. + // Assert that Self and Inner have the same pointer size, + // which is the best we can do to assert their metadata is the same type + // on stable. + assert!(size_of::<*mut Inner>() == size_of::<*mut Self>()); + + unsafe { + // A pointer cast doesn't work here because rustc can't tell that + // the vtables match (because of the `?Sized` restriction relaxation). + // A `transmute` doesn't work because the layout of Rc is unspecified. + // + // SAFETY: + // * The unsafe contract requires that pointers to Inner and Self have + // identical representations, and that the size and alignment of Inner + // and Self are the same, which meets the safety requirements of + // Rc::from_raw + let wrapper_ptr: *const Self = Rc::into_raw(s); + let inner_ptr: *const Inner = transmute!(wrapper_ptr); + Rc::from_raw(inner_ptr) + } + } + + /// Convert an [`Arc`] to the wrapper type into an `Arc` to the inner type. + #[inline] + #[cfg(target_has_atomic = "ptr")] + fn peel_arc(s: Arc) -> Arc { + // The unsafe contract requires that these two have + // identical representations, and thus identical pointer metadata. + // Assert that Self and Inner have the same pointer size, + // which is the best we can do to assert their metadata is the same type + // on stable. + assert!(size_of::<*mut Inner>() == size_of::<*mut Self>()); + + unsafe { + // A pointer cast doesn't work here because rustc can't tell that + // the vtables match (because of the `?Sized` restriction relaxation). + // A `transmute` doesn't work because the layout of Arc is unspecified. + // + // SAFETY: + // * The unsafe contract requires that pointers to Inner and Self have + // identical representations, and that the size and alignment of Inner + // and Self are the same, which meets the safety requirements of + // Arc::from_raw + let wrapper_ptr: *const Self = Arc::into_raw(s); + let inner_ptr: *const Inner = transmute!(wrapper_ptr); + Arc::from_raw(inner_ptr) + } + } +} + +impl> TransparentWrapperAlloc + for T +{ +} + +/// As `Box<[u8]>`, but remembers the original alignment. +pub struct BoxBytes { + // SAFETY: `ptr` is aligned to `layout.align()`, points to + // `layout.size()` initialized bytes, and, if `layout.size() > 0`, + // is owned and was allocated with the global allocator with `layout`. + ptr: NonNull, + layout: Layout, +} + +// SAFETY: `BoxBytes` is semantically a `Box<[u8], Global>` with a different allocation alignment, +// `Box<[u8], Global>` is `Send + Sync`, and changing the allocation alignment has no thread-safety implications. +unsafe impl Send for BoxBytes {} +// SAFETY: See `Send` impl +unsafe impl Sync for BoxBytes {} + +impl Deref for BoxBytes { + type Target = [u8]; + + fn deref(&self) -> &Self::Target { + // SAFETY: See type invariant. + unsafe { + core::slice::from_raw_parts(self.ptr.as_ptr(), self.layout.size()) + } + } +} + +impl DerefMut for BoxBytes { + fn deref_mut(&mut self) -> &mut Self::Target { + // SAFETY: See type invariant. + unsafe { + core::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.layout.size()) + } + } +} + +impl Drop for BoxBytes { + fn drop(&mut self) { + if self.layout.size() != 0 { + // SAFETY: See type invariant: if `self.layout.size() != 0`, then + // `self.ptr` is owned and was allocated with `self.layout`. + unsafe { alloc::alloc::dealloc(self.ptr.as_ptr(), self.layout) }; + } + } +} + +impl From> for BoxBytes { + fn from(value: Box) -> Self { + value.box_bytes_of() + } +} + +mod sealed { + use crate::{BoxBytes, PodCastError}; + use alloc::boxed::Box; + + pub trait BoxBytesOf { + fn box_bytes_of(self: Box) -> BoxBytes; + } + + pub trait FromBoxBytes { + fn try_from_box_bytes( + bytes: BoxBytes, + ) -> Result, (PodCastError, BoxBytes)>; + } +} + +impl sealed::BoxBytesOf for T { + fn box_bytes_of(self: Box) -> BoxBytes { + let layout = Layout::new::(); + let ptr = Box::into_raw(self) as *mut u8; + // SAFETY: Box::into_raw() returns a non-null pointer. + let ptr = unsafe { NonNull::new_unchecked(ptr) }; + BoxBytes { ptr, layout } + } +} + +impl sealed::BoxBytesOf for [T] { + fn box_bytes_of(self: Box) -> BoxBytes { + let layout = Layout::for_value::<[T]>(&self); + let ptr = Box::into_raw(self) as *mut u8; + // SAFETY: Box::into_raw() returns a non-null pointer. + let ptr = unsafe { NonNull::new_unchecked(ptr) }; + BoxBytes { ptr, layout } + } +} + +impl sealed::BoxBytesOf for str { + fn box_bytes_of(self: Box) -> BoxBytes { + self.into_boxed_bytes().box_bytes_of() + } +} + +impl sealed::FromBoxBytes for T { + fn try_from_box_bytes( + bytes: BoxBytes, + ) -> Result, (PodCastError, BoxBytes)> { + let layout = Layout::new::(); + if bytes.layout.align() != layout.align() { + Err((PodCastError::AlignmentMismatch, bytes)) + } else if bytes.layout.size() != layout.size() { + Err((PodCastError::SizeMismatch, bytes)) + } else { + let (ptr, _) = bytes.into_raw_parts(); + // SAFETY: See BoxBytes type invariant. + Ok(unsafe { Box::from_raw(ptr.as_ptr() as *mut T) }) + } + } +} + +impl sealed::FromBoxBytes for [T] { + fn try_from_box_bytes( + bytes: BoxBytes, + ) -> Result, (PodCastError, BoxBytes)> { + let single_layout = Layout::new::(); + if bytes.layout.align() != single_layout.align() { + Err((PodCastError::AlignmentMismatch, bytes)) + } else if (single_layout.size() == 0 && bytes.layout.size() != 0) + || (single_layout.size() != 0 + && bytes.layout.size() % single_layout.size() != 0) + { + Err((PodCastError::OutputSliceWouldHaveSlop, bytes)) + } else { + let (ptr, layout) = bytes.into_raw_parts(); + let length = if single_layout.size() != 0 { + layout.size() / single_layout.size() + } else { + 0 + }; + let ptr = + core::ptr::slice_from_raw_parts_mut(ptr.as_ptr() as *mut T, length); + // SAFETY: See BoxBytes type invariant. + Ok(unsafe { Box::from_raw(ptr) }) + } + } +} + +/// Re-interprets `Box` as `BoxBytes`. +/// +/// `T` must be either [`Sized`] and [`NoUninit`], +/// [`[U]`](slice) where `U: NoUninit`, or [`str`]. +#[inline] +pub fn box_bytes_of(input: Box) -> BoxBytes { + input.box_bytes_of() +} + +/// Re-interprets `BoxBytes` as `Box`. +/// +/// `T` must be either [`Sized`] + [`AnyBitPattern`], or +/// [`[U]`](slice) where `U: AnyBitPattern`. +/// +/// ## Panics +/// +/// This is [`try_from_box_bytes`] but will panic on error and the input will be +/// dropped. +#[inline] +#[cfg_attr(feature = "track_caller", track_caller)] +pub fn from_box_bytes( + input: BoxBytes, +) -> Box { + try_from_box_bytes(input).map_err(|(error, _)| error).unwrap() +} + +/// Re-interprets `BoxBytes` as `Box`. +/// +/// `T` must be either [`Sized`] + [`AnyBitPattern`], or +/// [`[U]`](slice) where `U: AnyBitPattern`. +/// +/// Returns `Err`: +/// * If the input isn't aligned for `T`. +/// * If `T: Sized` and the input's length isn't exactly the size of `T`. +/// * If `T = [U]` and the input's length isn't exactly a multiple of the size +/// of `U`. +#[inline] +pub fn try_from_box_bytes( + input: BoxBytes, +) -> Result, (PodCastError, BoxBytes)> { + T::try_from_box_bytes(input) +} + +impl BoxBytes { + /// Constructs a `BoxBytes` from its raw parts. + /// + /// # Safety + /// + /// The pointer is owned, has been allocated with the provided layout, and + /// points to `layout.size()` initialized bytes. + pub unsafe fn from_raw_parts(ptr: NonNull, layout: Layout) -> Self { + BoxBytes { ptr, layout } + } + + /// Deconstructs a `BoxBytes` into its raw parts. + /// + /// The pointer is owned, has been allocated with the provided layout, and + /// points to `layout.size()` initialized bytes. + pub fn into_raw_parts(self) -> (NonNull, Layout) { + let me = ManuallyDrop::new(self); + (me.ptr, me.layout) + } + + /// Returns the original layout. + pub fn layout(&self) -> Layout { + self.layout + } +} diff --git a/third_party/rust/bytemuck/src/anybitpattern.rs b/third_party/rust/bytemuck/src/anybitpattern.rs new file mode 100644 index 000000000000..8076f9cc11c6 --- /dev/null +++ b/third_party/rust/bytemuck/src/anybitpattern.rs @@ -0,0 +1,64 @@ +use crate::{Pod, Zeroable}; + +/// Marker trait for "plain old data" types that are valid for any bit pattern. +/// +/// The requirements for this is very similar to [`Pod`], except that the type +/// can allow uninit (or padding) bytes. This limits what you can do with a type +/// of this kind, but also broadens the included types to `repr(C)` `struct`s +/// that contain padding as well as `union`s. Notably, you can only cast +/// *immutable* references and *owned* values into [`AnyBitPattern`] types, not +/// *mutable* references. +/// +/// [`Pod`] is a subset of [`AnyBitPattern`], meaning that any `T: Pod` is also +/// [`AnyBitPattern`] but any `T: AnyBitPattern` is not necessarily [`Pod`]. +/// +/// [`AnyBitPattern`] is a subset of [`Zeroable`], meaning that any `T: +/// AnyBitPattern` is also [`Zeroable`], but any `T: Zeroable` is not +/// necessarily [`AnyBitPattern`] +/// +/// # Derive +/// +/// A `#[derive(AnyBitPattern)]` macro is provided under the `derive` feature +/// flag which will automatically validate the requirements of this trait and +/// implement the trait for you for both structs and enums. This is the +/// recommended method for implementing the trait, however it's also possible to +/// do manually. If you implement it manually, you *must* carefully follow the +/// below safety rules. +/// +/// * *NOTE: even `C-style`, fieldless enums are intentionally **excluded** from +/// this trait, since it is **unsound** for an enum to have a discriminant +/// value that is not one of its defined variants. +/// +/// # Safety +/// +/// Similar to [`Pod`] except we disregard the rule about it must not contain +/// uninit bytes. Still, this is a quite strong guarantee about a type, so *be +/// careful* when implementing it manually. +/// +/// * The type must be inhabited (eg: no +/// [Infallible](core::convert::Infallible)). +/// * The type must be valid for any bit pattern of its backing memory. +/// * Structs need to have all fields also be `AnyBitPattern`. +/// * It is disallowed for types to contain pointer types, `Cell`, `UnsafeCell`, +/// atomics, and any other forms of interior mutability. +/// * More precisely: A shared reference to the type must allow reads, and +/// *only* reads. RustBelt's separation logic is based on the notion that a +/// type is allowed to define a sharing predicate, its own invariant that must +/// hold for shared references, and this predicate is the reasoning that allow +/// it to deal with atomic and cells etc. We require the sharing predicate to +/// be trivial and permit only read-only access. +/// * There's probably more, don't mess it up (I mean it). +pub unsafe trait AnyBitPattern: + Zeroable + Sized + Copy + 'static +{ +} + +unsafe impl AnyBitPattern for T {} + +#[cfg(feature = "zeroable_maybe_uninit")] +#[cfg_attr( + feature = "nightly_docs", + doc(cfg(feature = "zeroable_maybe_uninit")) +)] +unsafe impl AnyBitPattern for core::mem::MaybeUninit where T: AnyBitPattern +{} diff --git a/third_party/rust/bytemuck/src/checked.rs b/third_party/rust/bytemuck/src/checked.rs new file mode 100644 index 000000000000..c81b449f15c5 --- /dev/null +++ b/third_party/rust/bytemuck/src/checked.rs @@ -0,0 +1,529 @@ +//! Checked versions of the casting functions exposed in crate root +//! that support [`CheckedBitPattern`] types. + +use crate::{ + internal::{self, something_went_wrong}, + AnyBitPattern, NoUninit, +}; + +/// A marker trait that allows types that have some invalid bit patterns to be +/// used in places that otherwise require [`AnyBitPattern`] or [`Pod`] types by +/// performing a runtime check on a perticular set of bits. This is particularly +/// useful for types like fieldless ('C-style') enums, [`char`], bool, and +/// structs containing them. +/// +/// To do this, we define a `Bits` type which is a type with equivalent layout +/// to `Self` other than the invalid bit patterns which disallow `Self` from +/// being [`AnyBitPattern`]. This `Bits` type must itself implement +/// [`AnyBitPattern`]. Then, we implement a function that checks whether a +/// certain instance of the `Bits` is also a valid bit pattern of `Self`. If +/// this check passes, then we can allow casting from the `Bits` to `Self` (and +/// therefore, any type which is able to be cast to `Bits` is also able to be +/// cast to `Self`). +/// +/// [`AnyBitPattern`] is a subset of [`CheckedBitPattern`], meaning that any `T: +/// AnyBitPattern` is also [`CheckedBitPattern`]. This means you can also use +/// any [`AnyBitPattern`] type in the checked versions of casting functions in +/// this module. If it's possible, prefer implementing [`AnyBitPattern`] for +/// your type directly instead of [`CheckedBitPattern`] as it gives greater +/// flexibility. +/// +/// # Derive +/// +/// A `#[derive(CheckedBitPattern)]` macro is provided under the `derive` +/// feature flag which will automatically validate the requirements of this +/// trait and implement the trait for you for both enums and structs. This is +/// the recommended method for implementing the trait, however it's also +/// possible to do manually. +/// +/// # Example +/// +/// If manually implementing the trait, we can do something like so: +/// +/// ```rust +/// use bytemuck::{CheckedBitPattern, NoUninit}; +/// +/// #[repr(u32)] +/// #[derive(Copy, Clone)] +/// enum MyEnum { +/// Variant0 = 0, +/// Variant1 = 1, +/// Variant2 = 2, +/// } +/// +/// unsafe impl CheckedBitPattern for MyEnum { +/// type Bits = u32; +/// +/// fn is_valid_bit_pattern(bits: &u32) -> bool { +/// match *bits { +/// 0 | 1 | 2 => true, +/// _ => false, +/// } +/// } +/// } +/// +/// // It is often useful to also implement `NoUninit` on our `CheckedBitPattern` types. +/// // This will allow us to do casting of mutable references (and mutable slices). +/// // It is not always possible to do so, but in this case we have no padding so it is. +/// unsafe impl NoUninit for MyEnum {} +/// ``` +/// +/// We can now use relevant casting functions. For example, +/// +/// ```rust +/// # use bytemuck::{CheckedBitPattern, NoUninit}; +/// # #[repr(u32)] +/// # #[derive(Copy, Clone, PartialEq, Eq, Debug)] +/// # enum MyEnum { +/// # Variant0 = 0, +/// # Variant1 = 1, +/// # Variant2 = 2, +/// # } +/// # unsafe impl NoUninit for MyEnum {} +/// # unsafe impl CheckedBitPattern for MyEnum { +/// # type Bits = u32; +/// # fn is_valid_bit_pattern(bits: &u32) -> bool { +/// # match *bits { +/// # 0 | 1 | 2 => true, +/// # _ => false, +/// # } +/// # } +/// # } +/// use bytemuck::{bytes_of, bytes_of_mut}; +/// use bytemuck::checked; +/// +/// let bytes = bytes_of(&2u32); +/// let result = checked::try_from_bytes::(bytes); +/// assert_eq!(result, Ok(&MyEnum::Variant2)); +/// +/// // Fails for invalid discriminant +/// let bytes = bytes_of(&100u32); +/// let result = checked::try_from_bytes::(bytes); +/// assert!(result.is_err()); +/// +/// // Since we implemented NoUninit, we can also cast mutably from an original type +/// // that is `NoUninit + AnyBitPattern`: +/// let mut my_u32 = 2u32; +/// { +/// let as_enum_mut = checked::cast_mut::<_, MyEnum>(&mut my_u32); +/// assert_eq!(as_enum_mut, &mut MyEnum::Variant2); +/// *as_enum_mut = MyEnum::Variant0; +/// } +/// assert_eq!(my_u32, 0u32); +/// ``` +/// +/// # Safety +/// +/// * `Self` *must* have the same layout as the specified `Bits` except for the +/// possible invalid bit patterns being checked during +/// [`is_valid_bit_pattern`]. +/// * This almost certainly means your type must be `#[repr(C)]` or a similar +/// specified repr, but if you think you know better, you probably don't. If +/// you still think you know better, be careful and have fun. And don't mess +/// it up (I mean it). +/// * If [`is_valid_bit_pattern`] returns true, then the bit pattern contained +/// in `bits` must also be valid for an instance of `Self`. +/// * Probably more, don't mess it up (I mean it 2.0) +/// +/// [`is_valid_bit_pattern`]: CheckedBitPattern::is_valid_bit_pattern +/// [`Pod`]: crate::Pod +pub unsafe trait CheckedBitPattern: Copy { + /// `Self` *must* have the same layout as the specified `Bits` except for + /// the possible invalid bit patterns being checked during + /// [`is_valid_bit_pattern`]. + /// + /// [`is_valid_bit_pattern`]: CheckedBitPattern::is_valid_bit_pattern + type Bits: AnyBitPattern; + + /// If this function returns true, then it must be valid to reinterpret `bits` + /// as `&Self`. + fn is_valid_bit_pattern(bits: &Self::Bits) -> bool; +} + +unsafe impl CheckedBitPattern for T { + type Bits = T; + + #[inline(always)] + fn is_valid_bit_pattern(_bits: &T) -> bool { + true + } +} + +unsafe impl CheckedBitPattern for char { + type Bits = u32; + + #[inline] + fn is_valid_bit_pattern(bits: &Self::Bits) -> bool { + core::char::from_u32(*bits).is_some() + } +} + +unsafe impl CheckedBitPattern for bool { + type Bits = u8; + + #[inline] + fn is_valid_bit_pattern(bits: &Self::Bits) -> bool { + // DO NOT use the `matches!` macro, it isn't 1.34 compatible. + match *bits { + 0 | 1 => true, + _ => false, + } + } +} + +// Rust 1.70.0 documents that NonZero[int] has the same layout as [int]. +macro_rules! impl_checked_for_nonzero { + ($($nonzero:ty: $primitive:ty),* $(,)?) => { + $( + unsafe impl CheckedBitPattern for $nonzero { + type Bits = $primitive; + + #[inline] + fn is_valid_bit_pattern(bits: &Self::Bits) -> bool { + *bits != 0 + } + } + )* + }; +} +impl_checked_for_nonzero! { + core::num::NonZeroU8: u8, + core::num::NonZeroI8: i8, + core::num::NonZeroU16: u16, + core::num::NonZeroI16: i16, + core::num::NonZeroU32: u32, + core::num::NonZeroI32: i32, + core::num::NonZeroU64: u64, + core::num::NonZeroI64: i64, + core::num::NonZeroI128: i128, + core::num::NonZeroU128: u128, + core::num::NonZeroUsize: usize, + core::num::NonZeroIsize: isize, +} + +/// The things that can go wrong when casting between [`CheckedBitPattern`] data +/// forms. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum CheckedCastError { + /// An error occurred during a true-[`Pod`] cast + /// + /// [`Pod`]: crate::Pod + PodCastError(crate::PodCastError), + /// When casting to a [`CheckedBitPattern`] type, it is possible that the + /// original data contains an invalid bit pattern. If so, the cast will + /// fail and this error will be returned. Will never happen on casts + /// between [`Pod`] types. + /// + /// [`Pod`]: crate::Pod + InvalidBitPattern, +} + +#[cfg(not(target_arch = "spirv"))] +impl core::fmt::Display for CheckedCastError { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "{:?}", self) + } +} +#[cfg(feature = "extern_crate_std")] +#[cfg_attr(feature = "nightly_docs", doc(cfg(feature = "extern_crate_std")))] +impl std::error::Error for CheckedCastError {} + +impl From for CheckedCastError { + fn from(err: crate::PodCastError) -> CheckedCastError { + CheckedCastError::PodCastError(err) + } +} + +/// Re-interprets `&[u8]` as `&T`. +/// +/// ## Failure +/// +/// * If the slice isn't aligned for the new type +/// * If the slice's length isn’t exactly the size of the new type +/// * If the slice contains an invalid bit pattern for `T` +#[inline] +pub fn try_from_bytes( + s: &[u8], +) -> Result<&T, CheckedCastError> { + let pod = crate::try_from_bytes(s)?; + + if ::is_valid_bit_pattern(pod) { + Ok(unsafe { &*(pod as *const ::Bits as *const T) }) + } else { + Err(CheckedCastError::InvalidBitPattern) + } +} + +/// Re-interprets `&mut [u8]` as `&mut T`. +/// +/// ## Failure +/// +/// * If the slice isn't aligned for the new type +/// * If the slice's length isn’t exactly the size of the new type +/// * If the slice contains an invalid bit pattern for `T` +#[inline] +pub fn try_from_bytes_mut( + s: &mut [u8], +) -> Result<&mut T, CheckedCastError> { + let pod = unsafe { internal::try_from_bytes_mut(s) }?; + + if ::is_valid_bit_pattern(pod) { + Ok(unsafe { &mut *(pod as *mut ::Bits as *mut T) }) + } else { + Err(CheckedCastError::InvalidBitPattern) + } +} + +/// Reads from the bytes as if they were a `T`. +/// +/// ## Failure +/// * If the `bytes` length is not equal to `size_of::()`. +/// * If the slice contains an invalid bit pattern for `T` +#[inline] +pub fn try_pod_read_unaligned( + bytes: &[u8], +) -> Result { + let pod = crate::try_pod_read_unaligned(bytes)?; + + if ::is_valid_bit_pattern(&pod) { + Ok(unsafe { transmute!(pod) }) + } else { + Err(CheckedCastError::InvalidBitPattern) + } +} + +/// Try to cast `A` into `B`. +/// +/// Note that for this particular type of cast, alignment isn't a factor. The +/// input value is semantically copied into the function and then returned to a +/// new memory location which will have whatever the required alignment of the +/// output type is. +/// +/// ## Failure +/// +/// * If the types don't have the same size this fails. +/// * If `a` contains an invalid bit pattern for `B` this fails. +#[inline] +pub fn try_cast( + a: A, +) -> Result { + let pod = crate::try_cast(a)?; + + if ::is_valid_bit_pattern(&pod) { + Ok(unsafe { transmute!(pod) }) + } else { + Err(CheckedCastError::InvalidBitPattern) + } +} + +/// Try to convert a `&A` into `&B`. +/// +/// ## Failure +/// +/// * If the reference isn't aligned in the new type +/// * If the source type and target type aren't the same size. +/// * If `a` contains an invalid bit pattern for `B` this fails. +#[inline] +pub fn try_cast_ref( + a: &A, +) -> Result<&B, CheckedCastError> { + let pod = crate::try_cast_ref(a)?; + + if ::is_valid_bit_pattern(pod) { + Ok(unsafe { &*(pod as *const ::Bits as *const B) }) + } else { + Err(CheckedCastError::InvalidBitPattern) + } +} + +/// Try to convert a `&mut A` into `&mut B`. +/// +/// As [`try_cast_ref`], but `mut`. +#[inline] +pub fn try_cast_mut< + A: NoUninit + AnyBitPattern, + B: CheckedBitPattern + NoUninit, +>( + a: &mut A, +) -> Result<&mut B, CheckedCastError> { + let pod = unsafe { internal::try_cast_mut(a) }?; + + if ::is_valid_bit_pattern(pod) { + Ok(unsafe { &mut *(pod as *mut ::Bits as *mut B) }) + } else { + Err(CheckedCastError::InvalidBitPattern) + } +} + +/// Try to convert `&[A]` into `&[B]` (possibly with a change in length). +/// +/// * `input.as_ptr() as usize == output.as_ptr() as usize` +/// * `input.len() * size_of::() == output.len() * size_of::()` +/// +/// ## Failure +/// +/// * If the target type has a greater alignment requirement and the input slice +/// isn't aligned. +/// * If the target element type is a different size from the current element +/// type, and the output slice wouldn't be a whole number of elements when +/// accounting for the size change (eg: 3 `u16` values is 1.5 `u32` values, so +/// that's a failure). +/// * If any element of the converted slice would contain an invalid bit pattern +/// for `B` this fails. +#[inline] +pub fn try_cast_slice( + a: &[A], +) -> Result<&[B], CheckedCastError> { + let pod = crate::try_cast_slice(a)?; + + if pod.iter().all(|pod| ::is_valid_bit_pattern(pod)) { + Ok(unsafe { + core::slice::from_raw_parts(pod.as_ptr() as *const B, pod.len()) + }) + } else { + Err(CheckedCastError::InvalidBitPattern) + } +} + +/// Try to convert `&mut [A]` into `&mut [B]` (possibly with a change in +/// length). +/// +/// As [`try_cast_slice`], but `&mut`. +#[inline] +pub fn try_cast_slice_mut< + A: NoUninit + AnyBitPattern, + B: CheckedBitPattern + NoUninit, +>( + a: &mut [A], +) -> Result<&mut [B], CheckedCastError> { + let pod = unsafe { internal::try_cast_slice_mut(a) }?; + + if pod.iter().all(|pod| ::is_valid_bit_pattern(pod)) { + Ok(unsafe { + core::slice::from_raw_parts_mut(pod.as_mut_ptr() as *mut B, pod.len()) + }) + } else { + Err(CheckedCastError::InvalidBitPattern) + } +} + +/// Re-interprets `&[u8]` as `&T`. +/// +/// ## Panics +/// +/// This is [`try_from_bytes`] but will panic on error. +#[inline] +#[cfg_attr(feature = "track_caller", track_caller)] +pub fn from_bytes(s: &[u8]) -> &T { + match try_from_bytes(s) { + Ok(t) => t, + Err(e) => something_went_wrong("from_bytes", e), + } +} + +/// Re-interprets `&mut [u8]` as `&mut T`. +/// +/// ## Panics +/// +/// This is [`try_from_bytes_mut`] but will panic on error. +#[inline] +#[cfg_attr(feature = "track_caller", track_caller)] +pub fn from_bytes_mut(s: &mut [u8]) -> &mut T { + match try_from_bytes_mut(s) { + Ok(t) => t, + Err(e) => something_went_wrong("from_bytes_mut", e), + } +} + +/// Reads the slice into a `T` value. +/// +/// ## Panics +/// * This is like [`try_pod_read_unaligned`] but will panic on failure. +#[inline] +#[cfg_attr(feature = "track_caller", track_caller)] +pub fn pod_read_unaligned(bytes: &[u8]) -> T { + match try_pod_read_unaligned(bytes) { + Ok(t) => t, + Err(e) => something_went_wrong("pod_read_unaligned", e), + } +} + +/// Cast `A` into `B` +/// +/// ## Panics +/// +/// * This is like [`try_cast`], but will panic on a size mismatch. +#[inline] +#[cfg_attr(feature = "track_caller", track_caller)] +pub fn cast(a: A) -> B { + match try_cast(a) { + Ok(t) => t, + Err(e) => something_went_wrong("cast", e), + } +} + +/// Cast `&mut A` into `&mut B`. +/// +/// ## Panics +/// +/// This is [`try_cast_mut`] but will panic on error. +#[inline] +#[cfg_attr(feature = "track_caller", track_caller)] +pub fn cast_mut< + A: NoUninit + AnyBitPattern, + B: NoUninit + CheckedBitPattern, +>( + a: &mut A, +) -> &mut B { + match try_cast_mut(a) { + Ok(t) => t, + Err(e) => something_went_wrong("cast_mut", e), + } +} + +/// Cast `&A` into `&B`. +/// +/// ## Panics +/// +/// This is [`try_cast_ref`] but will panic on error. +#[inline] +#[cfg_attr(feature = "track_caller", track_caller)] +pub fn cast_ref(a: &A) -> &B { + match try_cast_ref(a) { + Ok(t) => t, + Err(e) => something_went_wrong("cast_ref", e), + } +} + +/// Cast `&[A]` into `&[B]`. +/// +/// ## Panics +/// +/// This is [`try_cast_slice`] but will panic on error. +#[inline] +#[cfg_attr(feature = "track_caller", track_caller)] +pub fn cast_slice(a: &[A]) -> &[B] { + match try_cast_slice(a) { + Ok(t) => t, + Err(e) => something_went_wrong("cast_slice", e), + } +} + +/// Cast `&mut [A]` into `&mut [B]`. +/// +/// ## Panics +/// +/// This is [`try_cast_slice_mut`] but will panic on error. +#[inline] +#[cfg_attr(feature = "track_caller", track_caller)] +pub fn cast_slice_mut< + A: NoUninit + AnyBitPattern, + B: NoUninit + CheckedBitPattern, +>( + a: &mut [A], +) -> &mut [B] { + match try_cast_slice_mut(a) { + Ok(t) => t, + Err(e) => something_went_wrong("cast_slice_mut", e), + } +} diff --git a/third_party/rust/bytemuck/src/contiguous.rs b/third_party/rust/bytemuck/src/contiguous.rs new file mode 100644 index 000000000000..1ff0e23bff5a --- /dev/null +++ b/third_party/rust/bytemuck/src/contiguous.rs @@ -0,0 +1,206 @@ +#![allow(clippy::legacy_numeric_constants)] + +use super::*; + +/// A trait indicating that: +/// +/// 1. A type has an equivalent representation to some known integral type. +/// 2. All instances of this type fall in a fixed range of values. +/// 3. Within that range, there are no gaps. +/// +/// This is generally useful for fieldless enums (aka "c-style" enums), however +/// it's important that it only be used for those with an explicit `#[repr]`, as +/// `#[repr(Rust)]` fieldess enums have an unspecified layout. +/// +/// Additionally, you shouldn't assume that all implementations are enums. Any +/// type which meets the requirements above while following the rules under +/// "Safety" below is valid. +/// +/// # Example +/// +/// ``` +/// # use bytemuck::Contiguous; +/// #[repr(u8)] +/// #[derive(Debug, Copy, Clone, PartialEq)] +/// enum Foo { +/// A = 0, +/// B = 1, +/// C = 2, +/// D = 3, +/// E = 4, +/// } +/// unsafe impl Contiguous for Foo { +/// type Int = u8; +/// const MIN_VALUE: u8 = Foo::A as u8; +/// const MAX_VALUE: u8 = Foo::E as u8; +/// } +/// assert_eq!(Foo::from_integer(3).unwrap(), Foo::D); +/// assert_eq!(Foo::from_integer(8), None); +/// assert_eq!(Foo::C.into_integer(), 2); +/// ``` +/// # Safety +/// +/// This is an unsafe trait, and incorrectly implementing it is undefined +/// behavior. +/// +/// Informally, by implementing it, you're asserting that `C` is identical to +/// the integral type `C::Int`, and that every `C` falls between `C::MIN_VALUE` +/// and `C::MAX_VALUE` exactly once, without any gaps. +/// +/// Precisely, the guarantees you must uphold when implementing `Contiguous` for +/// some type `C` are: +/// +/// 1. The size of `C` and `C::Int` must be the same, and neither may be a ZST. +/// (Note: alignment is explicitly allowed to differ) +/// +/// 2. `C::Int` must be a primitive integer, and not a wrapper type. In the +/// future, this may be lifted to include cases where the behavior is +/// identical for a relevant set of traits (Ord, arithmetic, ...). +/// +/// 3. All `C::Int`s which are in the *inclusive* range between `C::MIN_VALUE` +/// and `C::MAX_VALUE` are bitwise identical to unique valid instances of +/// `C`. +/// +/// 4. There exist no instances of `C` such that their bitpatterns, when +/// interpreted as instances of `C::Int`, fall outside of the `MAX_VALUE` / +/// `MIN_VALUE` range -- It is legal for unsafe code to assume that if it +/// gets a `C` that implements `Contiguous`, it is in the appropriate range. +/// +/// 5. Finally, you promise not to provide overridden implementations of +/// `Contiguous::from_integer` and `Contiguous::into_integer`. +/// +/// For clarity, the following rules could be derived from the above, but are +/// listed explicitly: +/// +/// - `C::MAX_VALUE` must be greater or equal to `C::MIN_VALUE` (therefore, `C` +/// must be an inhabited type). +/// +/// - There exist no two values between `MIN_VALUE` and `MAX_VALUE` such that +/// when interpreted as a `C` they are considered identical (by, say, match). +pub unsafe trait Contiguous: Copy + 'static { + /// The primitive integer type with an identical representation to this + /// type. + /// + /// Contiguous is broadly intended for use with fieldless enums, and for + /// these the correct integer type is easy: The enum should have a + /// `#[repr(Int)]` or `#[repr(C)]` attribute, (if it does not, it is + /// *unsound* to implement `Contiguous`!). + /// + /// - For `#[repr(Int)]`, use the listed `Int`. e.g. `#[repr(u8)]` should use + /// `type Int = u8`. + /// + /// - For `#[repr(C)]`, use whichever type the C compiler will use to + /// represent the given enum. This is usually `c_int` (from `std::os::raw` + /// or `libc`), but it's up to you to make the determination as the + /// implementer of the unsafe trait. + /// + /// For precise rules, see the list under "Safety" above. + type Int: Copy + Ord; + + /// The upper *inclusive* bound for valid instances of this type. + const MAX_VALUE: Self::Int; + + /// The lower *inclusive* bound for valid instances of this type. + const MIN_VALUE: Self::Int; + + /// If `value` is within the range for valid instances of this type, + /// returns `Some(converted_value)`, otherwise, returns `None`. + /// + /// This is a trait method so that you can write `value.into_integer()` in + /// your code. It is a contract of this trait that if you implement + /// `Contiguous` on your type you **must not** override this method. + /// + /// # Panics + /// + /// We will not panic for any correct implementation of `Contiguous`, but + /// *may* panic if we detect an incorrect one. + /// + /// This is undefined behavior regardless, so it could have been the nasal + /// demons at that point anyway ;). + #[inline] + #[cfg_attr(feature = "track_caller", track_caller)] + fn from_integer(value: Self::Int) -> Option { + // Guard against an illegal implementation of Contiguous. Annoyingly we + // can't rely on `transmute` to do this for us (see below), but + // whatever, this gets compiled into nothing in release. + assert!(size_of::() == size_of::()); + if Self::MIN_VALUE <= value && value <= Self::MAX_VALUE { + // SAFETY: We've checked their bounds (and their size, even though + // they've sworn under the Oath Of Unsafe Rust that that already + // matched) so this is allowed by `Contiguous`'s unsafe contract. + // + // So, the `transmute!`. ideally we'd use transmute here, which + // is more obviously safe. Sadly, we can't, as these types still + // have unspecified sizes. + Some(unsafe { transmute!(value) }) + } else { + None + } + } + + /// Perform the conversion from `C` into the underlying integral type. This + /// mostly exists otherwise generic code would need unsafe for the `value as + /// integer` + /// + /// This is a trait method so that you can write `value.into_integer()` in + /// your code. It is a contract of this trait that if you implement + /// `Contiguous` on your type you **must not** override this method. + /// + /// # Panics + /// + /// We will not panic for any correct implementation of `Contiguous`, but + /// *may* panic if we detect an incorrect one. + /// + /// This is undefined behavior regardless, so it could have been the nasal + /// demons at that point anyway ;). + #[inline] + #[cfg_attr(feature = "track_caller", track_caller)] + fn into_integer(self) -> Self::Int { + // Guard against an illegal implementation of Contiguous. Annoyingly we + // can't rely on `transmute` to do the size check for us (see + // `from_integer's comment`), but whatever, this gets compiled into + // nothing in release. Note that we don't check the result of cast + assert!(size_of::() == size_of::()); + + // SAFETY: The unsafe contract requires that these have identical + // representations, and that the range be entirely valid. Using + // transmute! instead of transmute here is annoying, but is required + // as `Self` and `Self::Int` have unspecified sizes still. + unsafe { transmute!(self) } + } +} + +macro_rules! impl_contiguous { + ($($src:ty as $repr:ident in [$min:expr, $max:expr];)*) => {$( + unsafe impl Contiguous for $src { + type Int = $repr; + const MAX_VALUE: $repr = $max; + const MIN_VALUE: $repr = $min; + } + )*}; +} + +impl_contiguous! { + bool as u8 in [0, 1]; + + u8 as u8 in [0, u8::max_value()]; + u16 as u16 in [0, u16::max_value()]; + u32 as u32 in [0, u32::max_value()]; + u64 as u64 in [0, u64::max_value()]; + u128 as u128 in [0, u128::max_value()]; + usize as usize in [0, usize::max_value()]; + + i8 as i8 in [i8::min_value(), i8::max_value()]; + i16 as i16 in [i16::min_value(), i16::max_value()]; + i32 as i32 in [i32::min_value(), i32::max_value()]; + i64 as i64 in [i64::min_value(), i64::max_value()]; + i128 as i128 in [i128::min_value(), i128::max_value()]; + isize as isize in [isize::min_value(), isize::max_value()]; + + NonZeroU8 as u8 in [1, u8::max_value()]; + NonZeroU16 as u16 in [1, u16::max_value()]; + NonZeroU32 as u32 in [1, u32::max_value()]; + NonZeroU64 as u64 in [1, u64::max_value()]; + NonZeroU128 as u128 in [1, u128::max_value()]; + NonZeroUsize as usize in [1, usize::max_value()]; +} diff --git a/third_party/rust/bytemuck/src/internal.rs b/third_party/rust/bytemuck/src/internal.rs new file mode 100644 index 000000000000..2e8607bf8e4d --- /dev/null +++ b/third_party/rust/bytemuck/src/internal.rs @@ -0,0 +1,406 @@ +//! Internal implementation of casting functions not bound by marker traits +//! and therefore marked as unsafe. This is used so that we don't need to +//! duplicate the business logic contained in these functions between the +//! versions exported in the crate root, `checked`, and `relaxed` modules. +#![allow(unused_unsafe)] + +use crate::PodCastError; +use core::{marker::*, mem::*}; + +/* + +Note(Lokathor): We've switched all of the `unwrap` to `match` because there is +apparently a bug: https://github.com/rust-lang/rust/issues/68667 +and it doesn't seem to show up in simple godbolt examples but has been reported +as having an impact when there's a cast mixed in with other more complicated +code around it. Rustc/LLVM ends up missing that the `Err` can't ever happen for +particular type combinations, and then it doesn't fully eliminated the panic +possibility code branch. + +*/ + +/// Immediately panics. +#[cfg(not(target_arch = "spirv"))] +#[cold] +#[inline(never)] +#[cfg_attr(feature = "track_caller", track_caller)] +pub(crate) fn something_went_wrong( + _src: &str, _err: D, +) -> ! { + // Note(Lokathor): Keeping the panic here makes the panic _formatting_ go + // here too, which helps assembly readability and also helps keep down + // the inline pressure. + panic!("{src}>{err}", src = _src, err = _err); +} + +/// Immediately panics. +#[cfg(target_arch = "spirv")] +#[cold] +#[inline(never)] +pub(crate) fn something_went_wrong(_src: &str, _err: D) -> ! { + // Note: On the spirv targets from [rust-gpu](https://github.com/EmbarkStudios/rust-gpu) + // panic formatting cannot be used. We we just give a generic error message + // The chance that the panicking version of these functions will ever get + // called on spir-v targets with invalid inputs is small, but giving a + // simple error message is better than no error message at all. + panic!("Called a panicing helper from bytemuck which paniced"); +} + +/// Re-interprets `&T` as `&[u8]`. +/// +/// Any ZST becomes an empty slice, and in that case the pointer value of that +/// empty slice might not match the pointer value of the input reference. +#[inline(always)] +pub(crate) unsafe fn bytes_of(t: &T) -> &[u8] { + match try_cast_slice::(core::slice::from_ref(t)) { + Ok(s) => s, + Err(_) => unreachable!(), + } +} + +/// Re-interprets `&mut T` as `&mut [u8]`. +/// +/// Any ZST becomes an empty slice, and in that case the pointer value of that +/// empty slice might not match the pointer value of the input reference. +#[inline] +pub(crate) unsafe fn bytes_of_mut(t: &mut T) -> &mut [u8] { + match try_cast_slice_mut::(core::slice::from_mut(t)) { + Ok(s) => s, + Err(_) => unreachable!(), + } +} + +/// Re-interprets `&[u8]` as `&T`. +/// +/// ## Panics +/// +/// This is [`try_from_bytes`] but will panic on error. +#[inline] +#[cfg_attr(feature = "track_caller", track_caller)] +pub(crate) unsafe fn from_bytes(s: &[u8]) -> &T { + match try_from_bytes(s) { + Ok(t) => t, + Err(e) => something_went_wrong("from_bytes", e), + } +} + +/// Re-interprets `&mut [u8]` as `&mut T`. +/// +/// ## Panics +/// +/// This is [`try_from_bytes_mut`] but will panic on error. +#[inline] +#[cfg_attr(feature = "track_caller", track_caller)] +pub(crate) unsafe fn from_bytes_mut(s: &mut [u8]) -> &mut T { + match try_from_bytes_mut(s) { + Ok(t) => t, + Err(e) => something_went_wrong("from_bytes_mut", e), + } +} + +/// Reads from the bytes as if they were a `T`. +/// +/// ## Failure +/// * If the `bytes` length is not equal to `size_of::()`. +#[inline] +pub(crate) unsafe fn try_pod_read_unaligned( + bytes: &[u8], +) -> Result { + if bytes.len() != size_of::() { + Err(PodCastError::SizeMismatch) + } else { + Ok(unsafe { (bytes.as_ptr() as *const T).read_unaligned() }) + } +} + +/// Reads the slice into a `T` value. +/// +/// ## Panics +/// * This is like `try_pod_read_unaligned` but will panic on failure. +#[inline] +#[cfg_attr(feature = "track_caller", track_caller)] +pub(crate) unsafe fn pod_read_unaligned(bytes: &[u8]) -> T { + match try_pod_read_unaligned(bytes) { + Ok(t) => t, + Err(e) => something_went_wrong("pod_read_unaligned", e), + } +} + +/// Checks if `ptr` is aligned to an `align` memory boundary. +/// +/// ## Panics +/// * If `align` is not a power of two. This includes when `align` is zero. +#[inline] +#[cfg_attr(feature = "track_caller", track_caller)] +pub(crate) fn is_aligned_to(ptr: *const (), align: usize) -> bool { + #[cfg(feature = "align_offset")] + { + // This is in a way better than `ptr as usize % align == 0`, + // because casting a pointer to an integer has the side effect that it + // exposes the pointer's provenance, which may theoretically inhibit + // some compiler optimizations. + ptr.align_offset(align) == 0 + } + #[cfg(not(feature = "align_offset"))] + { + ((ptr as usize) % align) == 0 + } +} + +/// Re-interprets `&[u8]` as `&T`. +/// +/// ## Failure +/// +/// * If the slice isn't aligned for the new type +/// * If the slice's length isn’t exactly the size of the new type +#[inline] +pub(crate) unsafe fn try_from_bytes( + s: &[u8], +) -> Result<&T, PodCastError> { + if s.len() != size_of::() { + Err(PodCastError::SizeMismatch) + } else if !is_aligned_to(s.as_ptr() as *const (), align_of::()) { + Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) + } else { + Ok(unsafe { &*(s.as_ptr() as *const T) }) + } +} + +/// Re-interprets `&mut [u8]` as `&mut T`. +/// +/// ## Failure +/// +/// * If the slice isn't aligned for the new type +/// * If the slice's length isn’t exactly the size of the new type +#[inline] +pub(crate) unsafe fn try_from_bytes_mut( + s: &mut [u8], +) -> Result<&mut T, PodCastError> { + if s.len() != size_of::() { + Err(PodCastError::SizeMismatch) + } else if !is_aligned_to(s.as_ptr() as *const (), align_of::()) { + Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) + } else { + Ok(unsafe { &mut *(s.as_mut_ptr() as *mut T) }) + } +} + +/// Cast `A` into `B` +/// +/// ## Panics +/// +/// * This is like [`try_cast`](try_cast), but will panic on a size mismatch. +#[inline] +#[cfg_attr(feature = "track_caller", track_caller)] +pub(crate) unsafe fn cast(a: A) -> B { + if size_of::() == size_of::() { + unsafe { transmute!(a) } + } else { + something_went_wrong("cast", PodCastError::SizeMismatch) + } +} + +/// Cast `&mut A` into `&mut B`. +/// +/// ## Panics +/// +/// This is [`try_cast_mut`] but will panic on error. +#[inline] +#[cfg_attr(feature = "track_caller", track_caller)] +pub(crate) unsafe fn cast_mut(a: &mut A) -> &mut B { + if size_of::() == size_of::() && align_of::() >= align_of::() { + // Plz mr compiler, just notice that we can't ever hit Err in this case. + match try_cast_mut(a) { + Ok(b) => b, + Err(_) => unreachable!(), + } + } else { + match try_cast_mut(a) { + Ok(b) => b, + Err(e) => something_went_wrong("cast_mut", e), + } + } +} + +/// Cast `&A` into `&B`. +/// +/// ## Panics +/// +/// This is [`try_cast_ref`] but will panic on error. +#[inline] +#[cfg_attr(feature = "track_caller", track_caller)] +pub(crate) unsafe fn cast_ref(a: &A) -> &B { + if size_of::() == size_of::() && align_of::() >= align_of::() { + // Plz mr compiler, just notice that we can't ever hit Err in this case. + match try_cast_ref(a) { + Ok(b) => b, + Err(_) => unreachable!(), + } + } else { + match try_cast_ref(a) { + Ok(b) => b, + Err(e) => something_went_wrong("cast_ref", e), + } + } +} + +/// Cast `&[A]` into `&[B]`. +/// +/// ## Panics +/// +/// This is [`try_cast_slice`] but will panic on error. +#[inline] +#[cfg_attr(feature = "track_caller", track_caller)] +pub(crate) unsafe fn cast_slice(a: &[A]) -> &[B] { + match try_cast_slice(a) { + Ok(b) => b, + Err(e) => something_went_wrong("cast_slice", e), + } +} + +/// Cast `&mut [A]` into `&mut [B]`. +/// +/// ## Panics +/// +/// This is [`try_cast_slice_mut`] but will panic on error. +#[inline] +#[cfg_attr(feature = "track_caller", track_caller)] +pub(crate) unsafe fn cast_slice_mut(a: &mut [A]) -> &mut [B] { + match try_cast_slice_mut(a) { + Ok(b) => b, + Err(e) => something_went_wrong("cast_slice_mut", e), + } +} + +/// Try to cast `A` into `B`. +/// +/// Note that for this particular type of cast, alignment isn't a factor. The +/// input value is semantically copied into the function and then returned to a +/// new memory location which will have whatever the required alignment of the +/// output type is. +/// +/// ## Failure +/// +/// * If the types don't have the same size this fails. +#[inline] +pub(crate) unsafe fn try_cast( + a: A, +) -> Result { + if size_of::() == size_of::() { + Ok(unsafe { transmute!(a) }) + } else { + Err(PodCastError::SizeMismatch) + } +} + +/// Try to convert a `&A` into `&B`. +/// +/// ## Failure +/// +/// * If the reference isn't aligned in the new type +/// * If the source type and target type aren't the same size. +#[inline] +pub(crate) unsafe fn try_cast_ref( + a: &A, +) -> Result<&B, PodCastError> { + // Note(Lokathor): everything with `align_of` and `size_of` will optimize away + // after monomorphization. + if align_of::() > align_of::() + && !is_aligned_to(a as *const A as *const (), align_of::()) + { + Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) + } else if size_of::() == size_of::() { + Ok(unsafe { &*(a as *const A as *const B) }) + } else { + Err(PodCastError::SizeMismatch) + } +} + +/// Try to convert a `&mut A` into `&mut B`. +/// +/// As [`try_cast_ref`], but `mut`. +#[inline] +pub(crate) unsafe fn try_cast_mut( + a: &mut A, +) -> Result<&mut B, PodCastError> { + // Note(Lokathor): everything with `align_of` and `size_of` will optimize away + // after monomorphization. + if align_of::() > align_of::() + && !is_aligned_to(a as *const A as *const (), align_of::()) + { + Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) + } else if size_of::() == size_of::() { + Ok(unsafe { &mut *(a as *mut A as *mut B) }) + } else { + Err(PodCastError::SizeMismatch) + } +} + +/// Try to convert `&[A]` into `&[B]` (possibly with a change in length). +/// +/// * `input.as_ptr() as usize == output.as_ptr() as usize` +/// * `input.len() * size_of::() == output.len() * size_of::()` +/// +/// ## Failure +/// +/// * If the target type has a greater alignment requirement and the input slice +/// isn't aligned. +/// * If the target element type is a different size from the current element +/// type, and the output slice wouldn't be a whole number of elements when +/// accounting for the size change (eg: 3 `u16` values is 1.5 `u32` values, so +/// that's a failure). +#[inline] +pub(crate) unsafe fn try_cast_slice( + a: &[A], +) -> Result<&[B], PodCastError> { + let input_bytes = core::mem::size_of_val::<[A]>(a); + // Note(Lokathor): everything with `align_of` and `size_of` will optimize away + // after monomorphization. + if align_of::() > align_of::() + && !is_aligned_to(a.as_ptr() as *const (), align_of::()) + { + Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) + } else if size_of::() == size_of::() { + Ok(unsafe { core::slice::from_raw_parts(a.as_ptr() as *const B, a.len()) }) + } else if (size_of::() != 0 && input_bytes % size_of::() == 0) + || (size_of::() == 0 && input_bytes == 0) + { + let new_len = + if size_of::() != 0 { input_bytes / size_of::() } else { 0 }; + Ok(unsafe { core::slice::from_raw_parts(a.as_ptr() as *const B, new_len) }) + } else { + Err(PodCastError::OutputSliceWouldHaveSlop) + } +} + +/// Try to convert `&mut [A]` into `&mut [B]` (possibly with a change in +/// length). +/// +/// As [`try_cast_slice`], but `&mut`. +#[inline] +pub(crate) unsafe fn try_cast_slice_mut( + a: &mut [A], +) -> Result<&mut [B], PodCastError> { + let input_bytes = core::mem::size_of_val::<[A]>(a); + // Note(Lokathor): everything with `align_of` and `size_of` will optimize away + // after monomorphization. + if align_of::() > align_of::() + && !is_aligned_to(a.as_ptr() as *const (), align_of::()) + { + Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) + } else if size_of::() == size_of::() { + Ok(unsafe { + core::slice::from_raw_parts_mut(a.as_mut_ptr() as *mut B, a.len()) + }) + } else if (size_of::() != 0 && input_bytes % size_of::() == 0) + || (size_of::() == 0 && input_bytes == 0) + { + let new_len = + if size_of::() != 0 { input_bytes / size_of::() } else { 0 }; + Ok(unsafe { + core::slice::from_raw_parts_mut(a.as_mut_ptr() as *mut B, new_len) + }) + } else { + Err(PodCastError::OutputSliceWouldHaveSlop) + } +} diff --git a/third_party/rust/bytemuck/src/lib.rs b/third_party/rust/bytemuck/src/lib.rs new file mode 100644 index 000000000000..7d3c18cdac57 --- /dev/null +++ b/third_party/rust/bytemuck/src/lib.rs @@ -0,0 +1,573 @@ +#![no_std] +#![warn(missing_docs)] +#![allow(unused_mut)] +#![allow(clippy::match_like_matches_macro)] +#![allow(clippy::uninlined_format_args)] +#![allow(clippy::result_unit_err)] +#![allow(clippy::type_complexity)] +#![cfg_attr(feature = "nightly_docs", feature(doc_cfg))] +#![cfg_attr(feature = "nightly_portable_simd", feature(portable_simd))] +#![cfg_attr(feature = "nightly_float", feature(f16, f128))] +#![cfg_attr( + all( + feature = "nightly_stdsimd", + any(target_arch = "x86_64", target_arch = "x86") + ), + feature(stdarch_x86_avx512) +)] + +//! This crate gives small utilities for casting between plain data types. +//! +//! ## Basics +//! +//! Data comes in five basic forms in Rust, so we have five basic casting +//! functions: +//! +//! * `T` uses [`cast`] +//! * `&T` uses [`cast_ref`] +//! * `&mut T` uses [`cast_mut`] +//! * `&[T]` uses [`cast_slice`] +//! * `&mut [T]` uses [`cast_slice_mut`] +//! +//! Depending on the function, the [`NoUninit`] and/or [`AnyBitPattern`] traits +//! are used to maintain memory safety. +//! +//! **Historical Note:** When the crate first started the [`Pod`] trait was used +//! instead, and so you may hear people refer to that, but it has the strongest +//! requirements and people eventually wanted the more fine-grained system, so +//! here we are. All types that impl `Pod` have a blanket impl to also support +//! `NoUninit` and `AnyBitPattern`. The traits unfortunately do not have a +//! perfectly clean hierarchy for semver reasons. +//! +//! ## Failures +//! +//! Some casts will never fail, and other casts might fail. +//! +//! * `cast::` always works (and [`f32::from_bits`]). +//! * `cast_ref::<[u8; 4], u32>` might fail if the specific array reference +//! given at runtime doesn't have alignment 4. +//! +//! In addition to the "normal" forms of each function, which will panic on +//! invalid input, there's also `try_` versions which will return a `Result`. +//! +//! If you would like to statically ensure that a cast will work at runtime you +//! can use the `must_cast` crate feature and the `must_` casting functions. A +//! "must cast" that can't be statically known to be valid will cause a +//! compilation error (and sometimes a very hard to read compilation error). +//! +//! ## Using Your Own Types +//! +//! All the functions listed above are guarded by the [`Pod`] trait, which is a +//! sub-trait of the [`Zeroable`] trait. +//! +//! If you enable the crate's `derive` feature then these traits can be derived +//! on your own types. The derive macros will perform the necessary checks on +//! your type declaration, and trigger an error if your type does not qualify. +//! +//! The derive macros might not cover all edge cases, and sometimes they will +//! error when actually everything is fine. As a last resort you can impl these +//! traits manually. However, these traits are `unsafe`, and you should +//! carefully read the requirements before using a manual implementation. +//! +//! ## Cargo Features +//! +//! The crate supports Rust 1.34 when no features are enabled, and so there's +//! cargo features for thing that you might consider "obvious". +//! +//! The cargo features **do not** promise any particular MSRV, and they may +//! increase their MSRV in new versions. +//! +//! * `derive`: Provide derive macros for the various traits. +//! * `extern_crate_alloc`: Provide utilities for `alloc` related types such as +//! Box and Vec. +//! * `zeroable_maybe_uninit` and `zeroable_atomics`: Provide more [`Zeroable`] +//! impls. +//! * `pod_saturating`: Provide more [`Pod`] and [`Zeroable`] impls. +//! * `wasm_simd` and `aarch64_simd`: Support more SIMD types. +//! * `min_const_generics`: Provides appropriate impls for arrays of all lengths +//! instead of just for a select list of array lengths. +//! * `must_cast`: Provides the `must_` functions, which will compile error if +//! the requested cast can't be statically verified. +//! * `const_zeroed`: Provides a const version of the `zeroed` function. +//! +//! ## Related Crates +//! +//! - [`pack1`](https://docs.rs/pack1), which contains `bytemuck`-compatible +//! packed little-endian, big-endian and native-endian integer and floating +//! point number types. + +#[cfg(all(target_arch = "aarch64", feature = "aarch64_simd"))] +use core::arch::aarch64; +#[cfg(all(target_arch = "wasm32", feature = "wasm_simd"))] +use core::arch::wasm32; +#[cfg(target_arch = "x86")] +use core::arch::x86; +#[cfg(target_arch = "x86_64")] +use core::arch::x86_64; +// +use core::{ + marker::*, + mem::{align_of, size_of}, + num::*, + ptr::*, +}; + +// Used from macros to ensure we aren't using some locally defined name and +// actually are referencing libcore. This also would allow pre-2018 edition +// crates to use our macros, but I'm not sure how important that is. +#[doc(hidden)] +pub use ::core as __core; + +#[cfg(not(feature = "min_const_generics"))] +macro_rules! impl_unsafe_marker_for_array { + ( $marker:ident , $( $n:expr ),* ) => { + $(unsafe impl $marker for [T; $n] where T: $marker {})* + } +} + +/// A macro to transmute between two types without requiring knowing size +/// statically. +macro_rules! transmute { + ($val:expr) => { + ::core::mem::transmute_copy(&::core::mem::ManuallyDrop::new($val)) + }; + // This arm is for use in const contexts, where the borrow required to use + // transmute_copy poses an issue since the compiler hedges that the type + // being borrowed could have interior mutability. + ($srcty:ty; $dstty:ty; $val:expr) => {{ + #[repr(C)] + union Transmute { + src: ::core::mem::ManuallyDrop, + dst: ::core::mem::ManuallyDrop, + } + ::core::mem::ManuallyDrop::into_inner( + Transmute::<$srcty, $dstty> { src: ::core::mem::ManuallyDrop::new($val) } + .dst, + ) + }}; +} + +/// A macro to implement marker traits for various simd types. +/// #[allow(unused)] because the impls are only compiled on relevant platforms +/// with relevant cargo features enabled. +#[allow(unused)] +macro_rules! impl_unsafe_marker_for_simd { + ($(#[cfg($cfg_predicate:meta)])? unsafe impl $trait:ident for $platform:ident :: {}) => {}; + ($(#[cfg($cfg_predicate:meta)])? unsafe impl $trait:ident for $platform:ident :: { $first_type:ident $(, $types:ident)* $(,)? }) => { + $( #[cfg($cfg_predicate)] )? + $( #[cfg_attr(feature = "nightly_docs", doc(cfg($cfg_predicate)))] )? + unsafe impl $trait for $platform::$first_type {} + $( #[cfg($cfg_predicate)] )? // To prevent recursion errors if nothing is going to be expanded anyway. + impl_unsafe_marker_for_simd!($( #[cfg($cfg_predicate)] )? unsafe impl $trait for $platform::{ $( $types ),* }); + }; +} + +/// A macro for conditionally const-ifying a function. +/// #[allow(unused)] because currently it is only used with the `must_cast` feature. +#[allow(unused)] +macro_rules! maybe_const_fn { + ( + #[cfg($cfg_predicate:meta)] + $(#[$attr:meta])* + $vis:vis $(unsafe $($unsafe:lifetime)?)? fn $name:ident $($rest:tt)* + ) => { + #[cfg($cfg_predicate)] + $(#[$attr])* + $vis const $(unsafe $($unsafe)?)? fn $name $($rest)* + + #[cfg(not($cfg_predicate))] + $(#[$attr])* + $vis $(unsafe $($unsafe)?)? fn $name $($rest)* + }; +} + +#[cfg(feature = "extern_crate_std")] +extern crate std; + +#[cfg(feature = "extern_crate_alloc")] +extern crate alloc; +#[cfg(feature = "extern_crate_alloc")] +#[cfg_attr(feature = "nightly_docs", doc(cfg(feature = "extern_crate_alloc")))] +pub mod allocation; +#[cfg(feature = "extern_crate_alloc")] +pub use allocation::*; + +mod anybitpattern; +pub use anybitpattern::*; + +pub mod checked; +pub use checked::CheckedBitPattern; + +mod internal; + +mod zeroable; +pub use zeroable::*; +mod zeroable_in_option; +pub use zeroable_in_option::*; + +mod pod; +pub use pod::*; +mod pod_in_option; +pub use pod_in_option::*; + +#[cfg(feature = "must_cast")] +mod must; +#[cfg(feature = "must_cast")] +#[cfg_attr(feature = "nightly_docs", doc(cfg(feature = "must_cast")))] +pub use must::*; + +mod no_uninit; +pub use no_uninit::*; + +mod contiguous; +pub use contiguous::*; + +mod offset_of; +// ^ no import, the module only has a macro_rules, which are cursed and don't +// follow normal import/export rules. + +mod transparent; +pub use transparent::*; + +#[cfg(feature = "derive")] +#[cfg_attr(feature = "nightly_docs", doc(cfg(feature = "derive")))] +pub use bytemuck_derive::{ + AnyBitPattern, ByteEq, ByteHash, CheckedBitPattern, Contiguous, NoUninit, + Pod, TransparentWrapper, Zeroable, +}; + +/// The things that can go wrong when casting between [`Pod`] data forms. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum PodCastError { + /// You tried to cast a reference into a reference to a type with a higher + /// alignment requirement but the input reference wasn't aligned. + TargetAlignmentGreaterAndInputNotAligned, + /// If the element size of a slice changes, then the output slice changes + /// length accordingly. If the output slice wouldn't be a whole number of + /// elements, then the conversion fails. + OutputSliceWouldHaveSlop, + /// When casting an individual `T`, `&T`, or `&mut T` value the + /// source size and destination size must be an exact match. + SizeMismatch, + /// For this type of cast the alignments must be exactly the same and they + /// were not so now you're sad. + /// + /// This error is generated **only** by operations that cast allocated types + /// (such as `Box` and `Vec`), because in that case the alignment must stay + /// exact. + AlignmentMismatch, +} +#[cfg(not(target_arch = "spirv"))] +impl core::fmt::Display for PodCastError { + fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { + write!(f, "{:?}", self) + } +} +#[cfg(feature = "extern_crate_std")] +#[cfg_attr(feature = "nightly_docs", doc(cfg(feature = "extern_crate_std")))] +impl std::error::Error for PodCastError {} + +/// Re-interprets `&T` as `&[u8]`. +/// +/// Any ZST becomes an empty slice, and in that case the pointer value of that +/// empty slice might not match the pointer value of the input reference. +#[inline] +pub fn bytes_of(t: &T) -> &[u8] { + unsafe { internal::bytes_of(t) } +} + +/// Re-interprets `&mut T` as `&mut [u8]`. +/// +/// Any ZST becomes an empty slice, and in that case the pointer value of that +/// empty slice might not match the pointer value of the input reference. +#[inline] +pub fn bytes_of_mut(t: &mut T) -> &mut [u8] { + unsafe { internal::bytes_of_mut(t) } +} + +/// Re-interprets `&[u8]` as `&T`. +/// +/// ## Panics +/// +/// This is like [`try_from_bytes`] but will panic on error. +#[inline] +#[cfg_attr(feature = "track_caller", track_caller)] +pub fn from_bytes(s: &[u8]) -> &T { + unsafe { internal::from_bytes(s) } +} + +/// Re-interprets `&mut [u8]` as `&mut T`. +/// +/// ## Panics +/// +/// This is like [`try_from_bytes_mut`] but will panic on error. +#[inline] +#[cfg_attr(feature = "track_caller", track_caller)] +pub fn from_bytes_mut(s: &mut [u8]) -> &mut T { + unsafe { internal::from_bytes_mut(s) } +} + +/// Reads from the bytes as if they were a `T`. +/// +/// Unlike [`from_bytes`], the slice doesn't need to respect alignment of `T`, +/// only sizes must match. +/// +/// ## Failure +/// * If the `bytes` length is not equal to `size_of::()`. +#[inline] +pub fn try_pod_read_unaligned( + bytes: &[u8], +) -> Result { + unsafe { internal::try_pod_read_unaligned(bytes) } +} + +/// Reads the slice into a `T` value. +/// +/// Unlike [`from_bytes`], the slice doesn't need to respect alignment of `T`, +/// only sizes must match. +/// +/// ## Panics +/// * This is like `try_pod_read_unaligned` but will panic on failure. +#[inline] +#[cfg_attr(feature = "track_caller", track_caller)] +pub fn pod_read_unaligned(bytes: &[u8]) -> T { + unsafe { internal::pod_read_unaligned(bytes) } +} + +/// Re-interprets `&[u8]` as `&T`. +/// +/// ## Failure +/// +/// * If the slice isn't aligned for the new type +/// * If the slice's length isn’t exactly the size of the new type +#[inline] +pub fn try_from_bytes(s: &[u8]) -> Result<&T, PodCastError> { + unsafe { internal::try_from_bytes(s) } +} + +/// Re-interprets `&mut [u8]` as `&mut T`. +/// +/// ## Failure +/// +/// * If the slice isn't aligned for the new type +/// * If the slice's length isn’t exactly the size of the new type +#[inline] +pub fn try_from_bytes_mut( + s: &mut [u8], +) -> Result<&mut T, PodCastError> { + unsafe { internal::try_from_bytes_mut(s) } +} + +/// Cast `A` into `B` +/// +/// ## Panics +/// +/// * This is like [`try_cast`], but will panic on a size mismatch. +#[inline] +#[cfg_attr(feature = "track_caller", track_caller)] +pub fn cast(a: A) -> B { + unsafe { internal::cast(a) } +} + +/// Cast `&mut A` into `&mut B`. +/// +/// ## Panics +/// +/// This is [`try_cast_mut`] but will panic on error. +#[inline] +#[cfg_attr(feature = "track_caller", track_caller)] +pub fn cast_mut( + a: &mut A, +) -> &mut B { + unsafe { internal::cast_mut(a) } +} + +/// Cast `&A` into `&B`. +/// +/// ## Panics +/// +/// This is [`try_cast_ref`] but will panic on error. +#[inline] +#[cfg_attr(feature = "track_caller", track_caller)] +pub fn cast_ref(a: &A) -> &B { + unsafe { internal::cast_ref(a) } +} + +/// Cast `&[A]` into `&[B]`. +/// +/// ## Panics +/// +/// This is [`try_cast_slice`] but will panic on error. +#[inline] +#[cfg_attr(feature = "track_caller", track_caller)] +pub fn cast_slice(a: &[A]) -> &[B] { + unsafe { internal::cast_slice(a) } +} + +/// Cast `&mut [A]` into `&mut [B]`. +/// +/// ## Panics +/// +/// This is [`try_cast_slice_mut`] but will panic on error. +#[inline] +#[cfg_attr(feature = "track_caller", track_caller)] +pub fn cast_slice_mut< + A: NoUninit + AnyBitPattern, + B: NoUninit + AnyBitPattern, +>( + a: &mut [A], +) -> &mut [B] { + unsafe { internal::cast_slice_mut(a) } +} + +/// As [`align_to`](https://doc.rust-lang.org/std/primitive.slice.html#method.align_to), +/// but safe because of the [`Pod`] bound. +#[inline] +pub fn pod_align_to( + vals: &[T], +) -> (&[T], &[U], &[T]) { + unsafe { vals.align_to::() } +} + +/// As [`align_to_mut`](https://doc.rust-lang.org/std/primitive.slice.html#method.align_to_mut), +/// but safe because of the [`Pod`] bound. +#[inline] +pub fn pod_align_to_mut< + T: NoUninit + AnyBitPattern, + U: NoUninit + AnyBitPattern, +>( + vals: &mut [T], +) -> (&mut [T], &mut [U], &mut [T]) { + unsafe { vals.align_to_mut::() } +} + +/// Try to cast `A` into `B`. +/// +/// Note that for this particular type of cast, alignment isn't a factor. The +/// input value is semantically copied into the function and then returned to a +/// new memory location which will have whatever the required alignment of the +/// output type is. +/// +/// ## Failure +/// +/// * If the types don't have the same size this fails. +#[inline] +pub fn try_cast( + a: A, +) -> Result { + unsafe { internal::try_cast(a) } +} + +/// Try to convert a `&A` into `&B`. +/// +/// ## Failure +/// +/// * If the reference isn't aligned in the new type +/// * If the source type and target type aren't the same size. +#[inline] +pub fn try_cast_ref( + a: &A, +) -> Result<&B, PodCastError> { + unsafe { internal::try_cast_ref(a) } +} + +/// Try to convert a `&mut A` into `&mut B`. +/// +/// As [`try_cast_ref`], but `mut`. +#[inline] +pub fn try_cast_mut< + A: NoUninit + AnyBitPattern, + B: NoUninit + AnyBitPattern, +>( + a: &mut A, +) -> Result<&mut B, PodCastError> { + unsafe { internal::try_cast_mut(a) } +} + +/// Try to convert `&[A]` into `&[B]` (possibly with a change in length). +/// +/// * `input.as_ptr() as usize == output.as_ptr() as usize` +/// * `input.len() * size_of::() == output.len() * size_of::()` +/// +/// ## Failure +/// +/// * If the target type has a greater alignment requirement and the input slice +/// isn't aligned. +/// * If the target element type is a different size from the current element +/// type, and the output slice wouldn't be a whole number of elements when +/// accounting for the size change (eg: 3 `u16` values is 1.5 `u32` values, so +/// that's a failure). +/// * Similarly, you can't convert between a [ZST](https://doc.rust-lang.org/nomicon/exotic-sizes.html#zero-sized-types-zsts) +/// and a non-ZST. +#[inline] +pub fn try_cast_slice( + a: &[A], +) -> Result<&[B], PodCastError> { + unsafe { internal::try_cast_slice(a) } +} + +/// Try to convert `&mut [A]` into `&mut [B]` (possibly with a change in +/// length). +/// +/// As [`try_cast_slice`], but `&mut`. +#[inline] +pub fn try_cast_slice_mut< + A: NoUninit + AnyBitPattern, + B: NoUninit + AnyBitPattern, +>( + a: &mut [A], +) -> Result<&mut [B], PodCastError> { + unsafe { internal::try_cast_slice_mut(a) } +} + +/// Fill all bytes of `target` with zeroes (see [`Zeroable`]). +/// +/// This is similar to `*target = Zeroable::zeroed()`, but guarantees that any +/// padding bytes in `target` are zeroed as well. +/// +/// See also [`fill_zeroes`], if you have a slice rather than a single value. +#[inline] +pub fn write_zeroes(target: &mut T) { + struct EnsureZeroWrite(*mut T); + impl Drop for EnsureZeroWrite { + #[inline(always)] + fn drop(&mut self) { + unsafe { + core::ptr::write_bytes(self.0, 0u8, 1); + } + } + } + unsafe { + let guard = EnsureZeroWrite(target); + core::ptr::drop_in_place(guard.0); + drop(guard); + } +} + +/// Fill all bytes of `slice` with zeroes (see [`Zeroable`]). +/// +/// This is similar to `slice.fill(Zeroable::zeroed())`, but guarantees that any +/// padding bytes in `slice` are zeroed as well. +/// +/// See also [`write_zeroes`], which zeroes all bytes of a single value rather +/// than a slice. +#[inline] +pub fn fill_zeroes(slice: &mut [T]) { + if core::mem::needs_drop::() { + // If `T` needs to be dropped then we have to do this one item at a time, in + // case one of the intermediate drops does a panic. + slice.iter_mut().for_each(write_zeroes); + } else { + // Otherwise we can be really fast and just fill everthing with zeros. + let len = slice.len(); + unsafe { core::ptr::write_bytes(slice.as_mut_ptr(), 0u8, len) } + } +} + +/// Same as [`Zeroable::zeroed`], but as a `const fn` const. +#[cfg(feature = "const_zeroed")] +#[inline] +#[must_use] +pub const fn zeroed() -> T { + unsafe { core::mem::zeroed() } +} diff --git a/third_party/rust/bytemuck/src/must.rs b/third_party/rust/bytemuck/src/must.rs new file mode 100644 index 000000000000..1bb5ccc9fd72 --- /dev/null +++ b/third_party/rust/bytemuck/src/must.rs @@ -0,0 +1,212 @@ +#![allow(clippy::module_name_repetitions)] +#![allow(clippy::let_unit_value)] +#![allow(clippy::let_underscore_untyped)] +#![allow(clippy::ptr_as_ptr)] + +use crate::{AnyBitPattern, NoUninit}; +use core::mem::{align_of, size_of}; + +struct Cast((A, B)); +impl Cast { + const ASSERT_ALIGN_GREATER_THAN_EQUAL: () = + assert!(align_of::() >= align_of::()); + const ASSERT_SIZE_EQUAL: () = assert!(size_of::() == size_of::()); + const ASSERT_SIZE_MULTIPLE_OF_OR_INPUT_ZST: () = assert!( + (size_of::() == 0) + || (size_of::() != 0 && size_of::() % size_of::() == 0) + ); +} + +/// Cast `A` into `B` if infalliable, or fail to compile. +/// +/// Note that for this particular type of cast, alignment isn't a factor. The +/// input value is semantically copied into the function and then returned to a +/// new memory location which will have whatever the required alignment of the +/// output type is. +/// +/// ## Failure +/// +/// * If the types don't have the same size this fails to compile. +/// +/// ## Examples +/// ``` +/// // compiles: +/// let bytes: [u8; 2] = bytemuck::must_cast(12_u16); +/// ``` +/// ```compile_fail,E0080 +/// // fails to compile (size mismatch): +/// let bytes : [u8; 3] = bytemuck::must_cast(12_u16); +/// ``` +#[inline] +pub const fn must_cast(a: A) -> B { + let _ = Cast::::ASSERT_SIZE_EQUAL; + unsafe { transmute!(A; B; a) } +} + +/// Convert `&A` into `&B` if infalliable, or fail to compile. +/// +/// ## Failure +/// +/// * If the target type has a greater alignment requirement. +/// * If the source type and target type aren't the same size. +/// +/// ## Examples +/// ``` +/// // compiles: +/// let bytes: &[u8; 2] = bytemuck::must_cast_ref(&12_u16); +/// ``` +/// ```compile_fail,E0080 +/// // fails to compile (size mismatch): +/// let bytes : &[u8; 3] = bytemuck::must_cast_ref(&12_u16); +/// ``` +/// ```compile_fail,E0080 +/// // fails to compile (alignment requirements increased): +/// let bytes : &u16 = bytemuck::must_cast_ref(&[1u8, 2u8]); +/// ``` +#[inline] +pub const fn must_cast_ref(a: &A) -> &B { + let _ = Cast::::ASSERT_SIZE_EQUAL; + let _ = Cast::::ASSERT_ALIGN_GREATER_THAN_EQUAL; + unsafe { &*(a as *const A as *const B) } +} + +maybe_const_fn! { + #[cfg(feature = "must_cast_extra")] + /// Convert a `&mut A` into `&mut B` if infalliable, or fail to compile. + /// + /// As [`must_cast_ref`], but `mut`. + /// + /// ## Examples + /// ``` + /// let mut i = 12_u16; + /// // compiles: + /// let bytes: &mut [u8; 2] = bytemuck::must_cast_mut(&mut i); + /// ``` + /// ```compile_fail,E0080 + /// # let mut bytes: &mut [u8; 2] = &mut [1, 2]; + /// // fails to compile (alignment requirements increased): + /// let i : &mut u16 = bytemuck::must_cast_mut(bytes); + /// ``` + /// ```compile_fail,E0080 + /// # let mut i = 12_u16; + /// // fails to compile (size mismatch): + /// let bytes : &mut [u8; 3] = bytemuck::must_cast_mut(&mut i); + /// ``` + #[inline] + pub fn must_cast_mut< + A: NoUninit + AnyBitPattern, + B: NoUninit + AnyBitPattern, + >( + a: &mut A, + ) -> &mut B { + let _ = Cast::::ASSERT_SIZE_EQUAL; + let _ = Cast::::ASSERT_ALIGN_GREATER_THAN_EQUAL; + unsafe { &mut *(a as *mut A as *mut B) } + } +} + +/// Convert `&[A]` into `&[B]` (possibly with a change in length) if +/// infalliable, or fail to compile. +/// +/// * `input.as_ptr() as usize == output.as_ptr() as usize` +/// * `input.len() * size_of::() == output.len() * size_of::()` +/// +/// ## Failure +/// +/// * If the target type has a greater alignment requirement. +/// * If the target element type doesn't evenly fit into the the current element +/// type (eg: 3 `u16` values is 1.5 `u32` values, so that's a failure). +/// * Similarly, you can't convert from a non-[ZST](https://doc.rust-lang.org/nomicon/exotic-sizes.html#zero-sized-types-zsts) +/// to a ZST (e.g. 3 `u8` values is not any number of `()` values). +/// +/// ## Examples +/// ``` +/// let indicies: &[u16] = &[1, 2, 3]; +/// // compiles: +/// let bytes: &[u8] = bytemuck::must_cast_slice(indicies); +/// ``` +/// ``` +/// let zsts: &[()] = &[(), (), ()]; +/// // compiles: +/// let bytes: &[u8] = bytemuck::must_cast_slice(zsts); +/// ``` +/// ```compile_fail,E0080 +/// # let bytes : &[u8] = &[1, 0, 2, 0, 3, 0]; +/// // fails to compile (bytes.len() might not be a multiple of 2): +/// let byte_pairs : &[[u8; 2]] = bytemuck::must_cast_slice(bytes); +/// ``` +/// ```compile_fail,E0080 +/// # let byte_pairs : &[[u8; 2]] = &[[1, 0], [2, 0], [3, 0]]; +/// // fails to compile (alignment requirements increased): +/// let indicies : &[u16] = bytemuck::must_cast_slice(byte_pairs); +/// ``` +/// ```compile_fail,E0080 +/// let bytes: &[u8] = &[]; +/// // fails to compile: (bytes.len() might not be 0) +/// let zsts: &[()] = bytemuck::must_cast_slice(bytes); +/// ``` +#[inline] +pub const fn must_cast_slice(a: &[A]) -> &[B] { + let _ = Cast::::ASSERT_SIZE_MULTIPLE_OF_OR_INPUT_ZST; + let _ = Cast::::ASSERT_ALIGN_GREATER_THAN_EQUAL; + let new_len = if size_of::() == size_of::() { + a.len() + } else { + a.len() * (size_of::() / size_of::()) + }; + unsafe { core::slice::from_raw_parts(a.as_ptr() as *const B, new_len) } +} + +maybe_const_fn! { + #[cfg(feature = "must_cast_extra")] + /// Convert `&mut [A]` into `&mut [B]` (possibly with a change in length) if + /// infalliable, or fail to compile. + /// + /// As [`must_cast_slice`], but `&mut`. + /// + /// ## Examples + /// ``` + /// let mut indicies = [1, 2, 3]; + /// let indicies: &mut [u16] = &mut indicies; + /// // compiles: + /// let bytes: &mut [u8] = bytemuck::must_cast_slice_mut(indicies); + /// ``` + /// ``` + /// let zsts: &mut [()] = &mut [(), (), ()]; + /// // compiles: + /// let bytes: &mut [u8] = bytemuck::must_cast_slice_mut(zsts); + /// ``` + /// ```compile_fail,E0080 + /// # let mut bytes = [1, 0, 2, 0, 3, 0]; + /// # let bytes : &mut [u8] = &mut bytes[..]; + /// // fails to compile (bytes.len() might not be a multiple of 2): + /// let byte_pairs : &mut [[u8; 2]] = bytemuck::must_cast_slice_mut(bytes); + /// ``` + /// ```compile_fail,E0080 + /// # let mut byte_pairs = [[1, 0], [2, 0], [3, 0]]; + /// # let byte_pairs : &mut [[u8; 2]] = &mut byte_pairs[..]; + /// // fails to compile (alignment requirements increased): + /// let indicies : &mut [u16] = bytemuck::must_cast_slice_mut(byte_pairs); + /// ``` + /// ```compile_fail,E0080 + /// let bytes: &mut [u8] = &mut []; + /// // fails to compile: (bytes.len() might not be 0) + /// let zsts: &mut [()] = bytemuck::must_cast_slice_mut(bytes); + /// ``` + #[inline] + pub fn must_cast_slice_mut< + A: NoUninit + AnyBitPattern, + B: NoUninit + AnyBitPattern, + >( + a: &mut [A], + ) -> &mut [B] { + let _ = Cast::::ASSERT_SIZE_MULTIPLE_OF_OR_INPUT_ZST; + let _ = Cast::::ASSERT_ALIGN_GREATER_THAN_EQUAL; + let new_len = if size_of::() == size_of::() { + a.len() + } else { + a.len() * (size_of::() / size_of::()) + }; + unsafe { core::slice::from_raw_parts_mut(a.as_mut_ptr() as *mut B, new_len) } + } +} diff --git a/third_party/rust/bytemuck/src/no_uninit.rs b/third_party/rust/bytemuck/src/no_uninit.rs new file mode 100644 index 000000000000..cc94b5261af7 --- /dev/null +++ b/third_party/rust/bytemuck/src/no_uninit.rs @@ -0,0 +1,80 @@ +use crate::Pod; +use core::num::{ + NonZeroI128, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI8, NonZeroIsize, + NonZeroU128, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU8, NonZeroUsize, +}; + +/// Marker trait for "plain old data" types with no uninit (or padding) bytes. +/// +/// The requirements for this is very similar to [`Pod`], +/// except that it doesn't require that all bit patterns of the type are valid, +/// i.e. it does not require the type to be [`Zeroable`][crate::Zeroable]. +/// This limits what you can do with a type of this kind, but also broadens the +/// included types to things like C-style enums. Notably, you can only cast from +/// *immutable* references to a [`NoUninit`] type into *immutable* references of +/// any other type, no casting of mutable references or mutable references to +/// slices etc. +/// +/// [`Pod`] is a subset of [`NoUninit`], meaning that any `T: Pod` is also +/// [`NoUninit`] but any `T: NoUninit` is not necessarily [`Pod`]. If possible, +/// prefer implementing [`Pod`] directly. To get more [`Pod`]-like functionality +/// for a type that is only [`NoUninit`], consider also implementing +/// [`CheckedBitPattern`][crate::CheckedBitPattern]. +/// +/// # Derive +/// +/// A `#[derive(NoUninit)]` macro is provided under the `derive` feature flag +/// which will automatically validate the requirements of this trait and +/// implement the trait for you for both enums and structs. This is the +/// recommended method for implementing the trait, however it's also possible to +/// do manually. If you implement it manually, you *must* carefully follow the +/// below safety rules. +/// +/// # Safety +/// +/// The same as [`Pod`] except we disregard the rule about it must +/// allow any bit pattern (i.e. it does not need to be +/// [`Zeroable`][crate::Zeroable]). Still, this is a quite strong guarantee +/// about a type, so *be careful* whem implementing it manually. +/// +/// * The type must be inhabited (eg: no +/// [Infallible](core::convert::Infallible)). +/// * The type must not contain any uninit (or padding) bytes, either in the +/// middle or on the end (eg: no `#[repr(C)] struct Foo(u8, u16)`, which has +/// padding in the middle, and also no `#[repr(C)] struct Foo(u16, u8)`, which +/// has padding on the end). +/// * Structs need to have all fields also be `NoUninit`. +/// * Structs need to be `repr(C)` or `repr(transparent)`. In the case of +/// `repr(C)`, the `packed` and `align` repr modifiers can be used as long as +/// all other rules end up being followed. +/// * Enums need to have an explicit `#[repr(Int)]` +/// * Enums must have only fieldless variants +/// * It is disallowed for types to contain pointer types, `Cell`, `UnsafeCell`, +/// atomics, and any other forms of interior mutability. +/// * More precisely: A shared reference to the type must allow reads, and +/// *only* reads. RustBelt's separation logic is based on the notion that a +/// type is allowed to define a sharing predicate, its own invariant that must +/// hold for shared references, and this predicate is the reasoning that allow +/// it to deal with atomic and cells etc. We require the sharing predicate to +/// be trivial and permit only read-only access. +/// * There's probably more, don't mess it up (I mean it). +pub unsafe trait NoUninit: Sized + Copy + 'static {} + +unsafe impl NoUninit for T {} + +unsafe impl NoUninit for char {} + +unsafe impl NoUninit for bool {} + +unsafe impl NoUninit for NonZeroU8 {} +unsafe impl NoUninit for NonZeroI8 {} +unsafe impl NoUninit for NonZeroU16 {} +unsafe impl NoUninit for NonZeroI16 {} +unsafe impl NoUninit for NonZeroU32 {} +unsafe impl NoUninit for NonZeroI32 {} +unsafe impl NoUninit for NonZeroU64 {} +unsafe impl NoUninit for NonZeroI64 {} +unsafe impl NoUninit for NonZeroU128 {} +unsafe impl NoUninit for NonZeroI128 {} +unsafe impl NoUninit for NonZeroUsize {} +unsafe impl NoUninit for NonZeroIsize {} diff --git a/third_party/rust/bytemuck/src/offset_of.rs b/third_party/rust/bytemuck/src/offset_of.rs new file mode 100644 index 000000000000..3de23276ef65 --- /dev/null +++ b/third_party/rust/bytemuck/src/offset_of.rs @@ -0,0 +1,135 @@ +#![forbid(unsafe_code)] + +/// Find the offset in bytes of the given `$field` of `$Type`. Requires an +/// already initialized `$instance` value to work with. +/// +/// This is similar to the macro from [`memoffset`](https://docs.rs/memoffset), +/// however it uses no `unsafe` code. +/// +/// This macro has a 3-argument and 2-argument version. +/// * In the 3-arg version you specify an instance of the type, the type itself, +/// and the field name. +/// * In the 2-arg version the macro will call the [`default`](Default::default) +/// method to make a temporary instance of the type for you. +/// +/// The output of this macro is the byte offset of the field (as a `usize`). The +/// calculations of the macro are fixed across the entire program, but if the +/// type used is `repr(Rust)` then they're *not* fixed across compilations or +/// compilers. +/// +/// ## Examples +/// +/// ### 3-arg Usage +/// +/// ```rust +/// # use bytemuck::offset_of; +/// // enums can't derive default, and for this example we don't pick one +/// enum MyExampleEnum { +/// A, +/// B, +/// C, +/// } +/// +/// // so now our struct here doesn't have Default +/// #[repr(C)] +/// struct MyNotDefaultType { +/// pub counter: i32, +/// pub some_field: MyExampleEnum, +/// } +/// +/// // but we provide an instance of the type and it's all good. +/// let val = MyNotDefaultType { counter: 5, some_field: MyExampleEnum::A }; +/// assert_eq!(offset_of!(val, MyNotDefaultType, some_field), 4); +/// ``` +/// +/// ### 2-arg Usage +/// +/// ```rust +/// # use bytemuck::offset_of; +/// #[derive(Default)] +/// #[repr(C)] +/// struct Vertex { +/// pub loc: [f32; 3], +/// pub color: [f32; 3], +/// } +/// // if the type impls Default the macro can make its own default instance. +/// assert_eq!(offset_of!(Vertex, loc), 0); +/// assert_eq!(offset_of!(Vertex, color), 12); +/// ``` +/// +/// # Usage with `#[repr(packed)]` structs +/// +/// Attempting to compute the offset of a `#[repr(packed)]` struct with +/// `bytemuck::offset_of!` requires an `unsafe` block. We hope to relax this in +/// the future, but currently it is required to work around a soundness hole in +/// Rust (See [rust-lang/rust#27060]). +/// +/// [rust-lang/rust#27060]: https://github.com/rust-lang/rust/issues/27060 +/// +///

+/// Warning: This is only true for versions of bytemuck > +/// 1.4.0. Previous versions of +/// bytemuck::offset_of! +/// will only emit a warning when used on the field of a packed struct in safe +/// code, which can lead to unsoundness. +///

+/// +/// For example, the following will fail to compile: +/// +/// ```compile_fail +/// #[repr(C, packed)] +/// #[derive(Default)] +/// struct Example { +/// field: u32, +/// } +/// // Doesn't compile: +/// let _offset = bytemuck::offset_of!(Example, field); +/// ``` +/// +/// While the error message this generates will mention the +/// `safe_packed_borrows` lint, the macro will still fail to compile even if +/// that lint is `#[allow]`ed: +/// +/// ```compile_fail +/// # #[repr(C, packed)] #[derive(Default)] struct Example { field: u32 } +/// // Still doesn't compile: +/// #[allow(safe_packed_borrows)] +/// { +/// let _offset = bytemuck::offset_of!(Example, field); +/// } +/// ``` +/// +/// This *can* be worked around by using `unsafe`, but it is only sound to do so +/// if you can guarantee that taking a reference to the field is sound. +/// +/// In practice, this means it only works for fields of align(1) types, or if +/// you know the field's offset in advance (defeating the point of `offset_of`) +/// and can prove that the struct's alignment and the field's offset are enough +/// to prove the field's alignment. +/// +/// Once the `raw_ref` macros are available, a future version of this crate will +/// use them to lift the limitations of packed structs. For the duration of the +/// `1.x` version of this crate that will be behind an on-by-default cargo +/// feature (to maintain minimum rust version support). +#[macro_export] +macro_rules! offset_of { + ($instance:expr, $Type:path, $field:tt) => {{ + #[forbid(safe_packed_borrows)] + { + // This helps us guard against field access going through a Deref impl. + #[allow(clippy::unneeded_field_pattern)] + let $Type { $field: _, .. }; + let reference: &$Type = &$instance; + let address = reference as *const _ as usize; + let field_pointer = &reference.$field as *const _ as usize; + // These asserts/unwraps are compiled away at release, and defend against + // the case where somehow a deref impl is still invoked. + let result = field_pointer.checked_sub(address).unwrap(); + assert!(result <= $crate::__core::mem::size_of::<$Type>()); + result + } + }}; + ($Type:path, $field:tt) => {{ + $crate::offset_of!(<$Type as Default>::default(), $Type, $field) + }}; +} diff --git a/third_party/rust/bytemuck/src/pod.rs b/third_party/rust/bytemuck/src/pod.rs new file mode 100644 index 000000000000..4b828d295df4 --- /dev/null +++ b/third_party/rust/bytemuck/src/pod.rs @@ -0,0 +1,184 @@ +use super::*; + +/// Marker trait for "plain old data". +/// +/// The point of this trait is that once something is marked "plain old data" +/// you can really go to town with the bit fiddling and bit casting. Therefore, +/// it's a relatively strong claim to make about a type. Do not add this to your +/// type casually. +/// +/// **Reminder:** The results of casting around bytes between data types are +/// _endian dependant_. Little-endian machines are the most common, but +/// big-endian machines do exist (and big-endian is also used for "network +/// order" bytes). +/// +/// ## Safety +/// +/// * The type must be inhabited (eg: no +/// [Infallible](core::convert::Infallible)). +/// * The type must allow any bit pattern (eg: no `bool` or `char`, which have +/// illegal bit patterns). +/// * The type must not contain any uninit (or padding) bytes, either in the +/// middle or on the end (eg: no `#[repr(C)] struct Foo(u8, u16)`, which has +/// padding in the middle, and also no `#[repr(C)] struct Foo(u16, u8)`, which +/// has padding on the end). +/// * The type needs to have all fields also be `Pod`. +/// * The type needs to be `repr(C)` or `repr(transparent)`. In the case of +/// `repr(C)`, the `packed` and `align` repr modifiers can be used as long as +/// all other rules end up being followed. +/// * It is disallowed for types to contain pointer types, `Cell`, `UnsafeCell`, +/// atomics, and any other forms of interior mutability. +/// * More precisely: A shared reference to the type must allow reads, and +/// *only* reads. RustBelt's separation logic is based on the notion that a +/// type is allowed to define a sharing predicate, its own invariant that must +/// hold for shared references, and this predicate is the reasoning that allow +/// it to deal with atomic and cells etc. We require the sharing predicate to +/// be trivial and permit only read-only access. +pub unsafe trait Pod: Zeroable + Copy + 'static {} + +unsafe impl Pod for () {} +unsafe impl Pod for u8 {} +unsafe impl Pod for i8 {} +unsafe impl Pod for u16 {} +unsafe impl Pod for i16 {} +unsafe impl Pod for u32 {} +unsafe impl Pod for i32 {} +unsafe impl Pod for u64 {} +unsafe impl Pod for i64 {} +unsafe impl Pod for usize {} +unsafe impl Pod for isize {} +unsafe impl Pod for u128 {} +unsafe impl Pod for i128 {} +#[cfg(feature = "nightly_float")] +unsafe impl Pod for f16 {} +unsafe impl Pod for f32 {} +unsafe impl Pod for f64 {} +#[cfg(feature = "nightly_float")] +unsafe impl Pod for f128 {} +unsafe impl Pod for Wrapping {} + +#[cfg(feature = "pod_saturating")] +unsafe impl Pod for core::num::Saturating{} + +#[cfg(feature = "unsound_ptr_pod_impl")] +#[cfg_attr( + feature = "nightly_docs", + doc(cfg(feature = "unsound_ptr_pod_impl")) +)] +unsafe impl Pod for *mut T {} +#[cfg(feature = "unsound_ptr_pod_impl")] +#[cfg_attr( + feature = "nightly_docs", + doc(cfg(feature = "unsound_ptr_pod_impl")) +)] +unsafe impl Pod for *const T {} +#[cfg(feature = "unsound_ptr_pod_impl")] +#[cfg_attr( + feature = "nightly_docs", + doc(cfg(feature = "unsound_ptr_pod_impl")) +)] +unsafe impl PodInOption for NonNull {} + +unsafe impl Pod for PhantomData {} +unsafe impl Pod for PhantomPinned {} +unsafe impl Pod for core::mem::ManuallyDrop {} + +// Note(Lokathor): MaybeUninit can NEVER be Pod. + +#[cfg(feature = "min_const_generics")] +#[cfg_attr(feature = "nightly_docs", doc(cfg(feature = "min_const_generics")))] +unsafe impl Pod for [T; N] where T: Pod {} + +#[cfg(not(feature = "min_const_generics"))] +impl_unsafe_marker_for_array!( + Pod, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 48, 64, 96, 128, 256, + 512, 1024, 2048, 4096 +); + +impl_unsafe_marker_for_simd!( + #[cfg(all(target_arch = "wasm32", feature = "wasm_simd"))] + unsafe impl Pod for wasm32::{v128} +); + +impl_unsafe_marker_for_simd!( + #[cfg(all(target_arch = "aarch64", feature = "aarch64_simd"))] + unsafe impl Pod for aarch64::{ + float32x2_t, float32x2x2_t, float32x2x3_t, float32x2x4_t, float32x4_t, + float32x4x2_t, float32x4x3_t, float32x4x4_t, float64x1_t, float64x1x2_t, + float64x1x3_t, float64x1x4_t, float64x2_t, float64x2x2_t, float64x2x3_t, + float64x2x4_t, int16x4_t, int16x4x2_t, int16x4x3_t, int16x4x4_t, int16x8_t, + int16x8x2_t, int16x8x3_t, int16x8x4_t, int32x2_t, int32x2x2_t, int32x2x3_t, + int32x2x4_t, int32x4_t, int32x4x2_t, int32x4x3_t, int32x4x4_t, int64x1_t, + int64x1x2_t, int64x1x3_t, int64x1x4_t, int64x2_t, int64x2x2_t, int64x2x3_t, + int64x2x4_t, int8x16_t, int8x16x2_t, int8x16x3_t, int8x16x4_t, int8x8_t, + int8x8x2_t, int8x8x3_t, int8x8x4_t, poly16x4_t, poly16x4x2_t, poly16x4x3_t, + poly16x4x4_t, poly16x8_t, poly16x8x2_t, poly16x8x3_t, poly16x8x4_t, + poly64x1_t, poly64x1x2_t, poly64x1x3_t, poly64x1x4_t, poly64x2_t, + poly64x2x2_t, poly64x2x3_t, poly64x2x4_t, poly8x16_t, poly8x16x2_t, + poly8x16x3_t, poly8x16x4_t, poly8x8_t, poly8x8x2_t, poly8x8x3_t, poly8x8x4_t, + uint16x4_t, uint16x4x2_t, uint16x4x3_t, uint16x4x4_t, uint16x8_t, + uint16x8x2_t, uint16x8x3_t, uint16x8x4_t, uint32x2_t, uint32x2x2_t, + uint32x2x3_t, uint32x2x4_t, uint32x4_t, uint32x4x2_t, uint32x4x3_t, + uint32x4x4_t, uint64x1_t, uint64x1x2_t, uint64x1x3_t, uint64x1x4_t, + uint64x2_t, uint64x2x2_t, uint64x2x3_t, uint64x2x4_t, uint8x16_t, + uint8x16x2_t, uint8x16x3_t, uint8x16x4_t, uint8x8_t, uint8x8x2_t, + uint8x8x3_t, uint8x8x4_t, + } +); + +impl_unsafe_marker_for_simd!( + #[cfg(target_arch = "x86")] + unsafe impl Pod for x86::{ + __m128i, __m128, __m128d, + __m256i, __m256, __m256d, + } +); + +impl_unsafe_marker_for_simd!( + #[cfg(target_arch = "x86_64")] + unsafe impl Pod for x86_64::{ + __m128i, __m128, __m128d, + __m256i, __m256, __m256d, + } +); + +#[cfg(feature = "nightly_portable_simd")] +#[cfg_attr( + feature = "nightly_docs", + doc(cfg(feature = "nightly_portable_simd")) +)] +unsafe impl Pod for core::simd::Simd +where + T: core::simd::SimdElement + Pod, + core::simd::LaneCount: core::simd::SupportedLaneCount, +{ +} + +impl_unsafe_marker_for_simd!( + #[cfg(all(target_arch = "x86", any(feature = "nightly_stdsimd", feature = "avx512_simd")))] + unsafe impl Pod for x86::{ + __m512, __m512d, __m512i + } +); + +impl_unsafe_marker_for_simd!( + #[cfg(all(target_arch = "x86_64", any(feature = "nightly_stdsimd", feature = "avx512_simd")))] + unsafe impl Pod for x86_64::{ + __m512, __m512d, __m512i + } +); + +impl_unsafe_marker_for_simd!( + #[cfg(all(target_arch = "x86", feature = "nightly_stdsimd"))] + unsafe impl Pod for x86::{ + __m128bh, __m256bh, __m512bh + } +); + +impl_unsafe_marker_for_simd!( + #[cfg(all(target_arch = "x86_64", feature = "nightly_stdsimd"))] + unsafe impl Pod for x86_64::{ + __m128bh, __m256bh, __m512bh + } +); diff --git a/third_party/rust/bytemuck/src/pod_in_option.rs b/third_party/rust/bytemuck/src/pod_in_option.rs new file mode 100644 index 000000000000..87ab93aded60 --- /dev/null +++ b/third_party/rust/bytemuck/src/pod_in_option.rs @@ -0,0 +1,27 @@ +use super::*; + +// Note(Lokathor): This is the neat part!! +unsafe impl Pod for Option {} + +/// Trait for types which are [Pod](Pod) when wrapped in +/// [Option](core::option::Option). +/// +/// ## Safety +/// +/// * `Option` must uphold the same invariants as [Pod](Pod). +/// * **Reminder:** pointers are **not** pod! **Do not** mix this trait with a +/// newtype over [NonNull](core::ptr::NonNull). +pub unsafe trait PodInOption: ZeroableInOption + Copy + 'static {} + +unsafe impl PodInOption for NonZeroI8 {} +unsafe impl PodInOption for NonZeroI16 {} +unsafe impl PodInOption for NonZeroI32 {} +unsafe impl PodInOption for NonZeroI64 {} +unsafe impl PodInOption for NonZeroI128 {} +unsafe impl PodInOption for NonZeroIsize {} +unsafe impl PodInOption for NonZeroU8 {} +unsafe impl PodInOption for NonZeroU16 {} +unsafe impl PodInOption for NonZeroU32 {} +unsafe impl PodInOption for NonZeroU64 {} +unsafe impl PodInOption for NonZeroU128 {} +unsafe impl PodInOption for NonZeroUsize {} diff --git a/third_party/rust/bytemuck/src/transparent.rs b/third_party/rust/bytemuck/src/transparent.rs new file mode 100644 index 000000000000..a1d37e07a9e2 --- /dev/null +++ b/third_party/rust/bytemuck/src/transparent.rs @@ -0,0 +1,322 @@ +use super::*; + +/// A trait which indicates that a type is a `#[repr(transparent)]` wrapper +/// around the `Inner` value. +/// +/// This allows safely copy transmuting between the `Inner` type and the +/// `TransparentWrapper` type. Functions like `wrap_{}` convert from the inner +/// type to the wrapper type and `peel_{}` functions do the inverse conversion +/// from the wrapper type to the inner type. We deliberately do not call the +/// wrapper-removing methods "unwrap" because at this point that word is too +/// strongly tied to the Option/ Result methods. +/// +/// # Safety +/// +/// The safety contract of `TransparentWrapper` is relatively simple: +/// +/// For a given `Wrapper` which implements `TransparentWrapper`: +/// +/// 1. `Wrapper` must be a wrapper around `Inner` with an identical data +/// representations. This either means that it must be a +/// `#[repr(transparent)]` struct which contains a either a field of type +/// `Inner` (or a field of some other transparent wrapper for `Inner`) as +/// the only non-ZST field. +/// +/// 2. Any fields *other* than the `Inner` field must be trivially constructable +/// ZSTs, for example `PhantomData`, `PhantomPinned`, etc. (When deriving +/// `TransparentWrapper` on a type with ZST fields, the ZST fields must be +/// [`Zeroable`]). +/// +/// 3. The `Wrapper` may not impose additional alignment requirements over +/// `Inner`. +/// - Note: this is currently guaranteed by `repr(transparent)`, but there +/// have been discussions of lifting it, so it's stated here explicitly. +/// +/// 4. All functions on `TransparentWrapper` **may not** be overridden. +/// +/// ## Caveats +/// +/// If the wrapper imposes additional constraints upon the inner type which are +/// required for safety, it's responsible for ensuring those still hold -- this +/// generally requires preventing access to instances of the inner type, as +/// implementing `TransparentWrapper for T` means anybody can call +/// `T::cast_ref(any_instance_of_u)`. +/// +/// For example, it would be invalid to implement TransparentWrapper for `str` +/// to implement `TransparentWrapper` around `[u8]` because of this. +/// +/// # Examples +/// +/// ## Basic +/// +/// ``` +/// use bytemuck::TransparentWrapper; +/// # #[derive(Default)] +/// # struct SomeStruct(u32); +/// +/// #[repr(transparent)] +/// struct MyWrapper(SomeStruct); +/// +/// unsafe impl TransparentWrapper for MyWrapper {} +/// +/// // interpret a reference to &SomeStruct as a &MyWrapper +/// let thing = SomeStruct::default(); +/// let inner_ref: &MyWrapper = MyWrapper::wrap_ref(&thing); +/// +/// // Works with &mut too. +/// let mut mut_thing = SomeStruct::default(); +/// let inner_mut: &mut MyWrapper = MyWrapper::wrap_mut(&mut mut_thing); +/// +/// # let _ = (inner_ref, inner_mut); // silence warnings +/// ``` +/// +/// ## Use with dynamically sized types +/// +/// ``` +/// use bytemuck::TransparentWrapper; +/// +/// #[repr(transparent)] +/// struct Slice([T]); +/// +/// unsafe impl TransparentWrapper<[T]> for Slice {} +/// +/// let s = Slice::wrap_ref(&[1u32, 2, 3]); +/// assert_eq!(&s.0, &[1, 2, 3]); +/// +/// let mut buf = [1, 2, 3u8]; +/// let sm = Slice::wrap_mut(&mut buf); +/// ``` +/// +/// ## Deriving +/// +/// When deriving, the non-wrapped fields must uphold all the normal +/// requirements, and must also be `Zeroable`. +#[cfg_attr(feature = "derive", doc = "```")] +#[cfg_attr( + not(feature = "derive"), + doc = "```ignore +// This example requires the `derive` feature." +)] +/// use bytemuck::TransparentWrapper; +/// use std::marker::PhantomData; +/// +/// #[derive(TransparentWrapper)] +/// #[repr(transparent)] +/// #[transparent(usize)] +/// struct Wrapper(usize, PhantomData); // PhantomData implements Zeroable for all T +/// ``` +/// +/// Here, an error will occur, because `MyZst` does not implement `Zeroable`. +#[cfg_attr(feature = "derive", doc = "```compile_fail")] +#[cfg_attr( + not(feature = "derive"), + doc = "```ignore +// This example requires the `derive` feature." +)] +/// use bytemuck::TransparentWrapper; +/// struct MyZst; +/// +/// #[derive(TransparentWrapper)] +/// #[repr(transparent)] +/// #[transparent(usize)] +/// struct Wrapper(usize, MyZst); // MyZst does not implement Zeroable +/// ``` +pub unsafe trait TransparentWrapper { + /// Convert the inner type into the wrapper type. + #[inline] + fn wrap(s: Inner) -> Self + where + Self: Sized, + Inner: Sized, + { + assert!(size_of::() == size_of::()); + assert!(align_of::() == align_of::()); + // SAFETY: The unsafe contract requires that `Self` and `Inner` have + // identical representations. + unsafe { transmute!(s) } + } + + /// Convert a reference to the inner type into a reference to the wrapper + /// type. + #[inline] + fn wrap_ref(s: &Inner) -> &Self { + // The unsafe contract requires that these two have + // identical representations, and thus identical pointer metadata. + // Assert that Self and Inner have the same pointer size, + // which is the best we can do to assert their metadata is the same type + // on stable. + assert!(size_of::<*const Inner>() == size_of::<*const Self>()); + unsafe { + // A pointer cast doesn't work here because rustc can't tell that + // the vtables match (because of the `?Sized` restriction relaxation). + // A `transmute` doesn't work because the sizes are unspecified. + // + // SAFETY: The unsafe contract requires that these two have + // identical representations. + let inner_ptr = s as *const Inner; + let wrapper_ptr: *const Self = transmute!(inner_ptr); + &*wrapper_ptr + } + } + + /// Convert a mutable reference to the inner type into a mutable reference to + /// the wrapper type. + #[inline] + fn wrap_mut(s: &mut Inner) -> &mut Self { + // The unsafe contract requires that these two have + // identical representations, and thus identical pointer metadata. + // Assert that Self and Inner have the same pointer size, + // which is about the best we can do on stable. + assert!(size_of::<*mut Inner>() == size_of::<*mut Self>()); + unsafe { + // A pointer cast doesn't work here because rustc can't tell that + // the vtables match (because of the `?Sized` restriction relaxation). + // A `transmute` doesn't work because the sizes are unspecified. + // + // SAFETY: The unsafe contract requires that these two have + // identical representations. + let inner_ptr = s as *mut Inner; + let wrapper_ptr: *mut Self = transmute!(inner_ptr); + &mut *wrapper_ptr + } + } + + /// Convert a slice to the inner type into a slice to the wrapper type. + #[inline] + fn wrap_slice(s: &[Inner]) -> &[Self] + where + Self: Sized, + Inner: Sized, + { + assert!(size_of::() == size_of::()); + assert!(align_of::() == align_of::()); + // SAFETY: The unsafe contract requires that these two have + // identical representations (size and alignment). + unsafe { core::slice::from_raw_parts(s.as_ptr() as *const Self, s.len()) } + } + + /// Convert a mutable slice to the inner type into a mutable slice to the + /// wrapper type. + #[inline] + fn wrap_slice_mut(s: &mut [Inner]) -> &mut [Self] + where + Self: Sized, + Inner: Sized, + { + assert!(size_of::() == size_of::()); + assert!(align_of::() == align_of::()); + // SAFETY: The unsafe contract requires that these two have + // identical representations (size and alignment). + unsafe { + core::slice::from_raw_parts_mut(s.as_mut_ptr() as *mut Self, s.len()) + } + } + + /// Convert the wrapper type into the inner type. + #[inline] + fn peel(s: Self) -> Inner + where + Self: Sized, + Inner: Sized, + { + assert!(size_of::() == size_of::()); + assert!(align_of::() == align_of::()); + // SAFETY: The unsafe contract requires that `Self` and `Inner` have + // identical representations. + unsafe { transmute!(s) } + } + + /// Convert a reference to the wrapper type into a reference to the inner + /// type. + #[inline] + fn peel_ref(s: &Self) -> &Inner { + // The unsafe contract requires that these two have + // identical representations, and thus identical pointer metadata. + // Assert that Self and Inner have the same pointer size, + // which is about the best we can do on stable. + assert!(size_of::<*const Inner>() == size_of::<*const Self>()); + unsafe { + // A pointer cast doesn't work here because rustc can't tell that + // the vtables match (because of the `?Sized` restriction relaxation). + // A `transmute` doesn't work because the sizes are unspecified. + // + // SAFETY: The unsafe contract requires that these two have + // identical representations. + let wrapper_ptr = s as *const Self; + let inner_ptr: *const Inner = transmute!(wrapper_ptr); + &*inner_ptr + } + } + + /// Convert a mutable reference to the wrapper type into a mutable reference + /// to the inner type. + #[inline] + fn peel_mut(s: &mut Self) -> &mut Inner { + // The unsafe contract requires that these two have + // identical representations, and thus identical pointer metadata. + // Assert that Self and Inner have the same pointer size, + // which is about the best we can do on stable. + assert!(size_of::<*mut Inner>() == size_of::<*mut Self>()); + unsafe { + // A pointer cast doesn't work here because rustc can't tell that + // the vtables match (because of the `?Sized` restriction relaxation). + // A `transmute` doesn't work because the sizes are unspecified. + // + // SAFETY: The unsafe contract requires that these two have + // identical representations. + let wrapper_ptr = s as *mut Self; + let inner_ptr: *mut Inner = transmute!(wrapper_ptr); + &mut *inner_ptr + } + } + + /// Convert a slice to the wrapped type into a slice to the inner type. + #[inline] + fn peel_slice(s: &[Self]) -> &[Inner] + where + Self: Sized, + Inner: Sized, + { + assert!(size_of::() == size_of::()); + assert!(align_of::() == align_of::()); + // SAFETY: The unsafe contract requires that these two have + // identical representations (size and alignment). + unsafe { core::slice::from_raw_parts(s.as_ptr() as *const Inner, s.len()) } + } + + /// Convert a mutable slice to the wrapped type into a mutable slice to the + /// inner type. + #[inline] + fn peel_slice_mut(s: &mut [Self]) -> &mut [Inner] + where + Self: Sized, + Inner: Sized, + { + assert!(size_of::() == size_of::()); + assert!(align_of::() == align_of::()); + // SAFETY: The unsafe contract requires that these two have + // identical representations (size and alignment). + unsafe { + core::slice::from_raw_parts_mut(s.as_mut_ptr() as *mut Inner, s.len()) + } + } +} + +unsafe impl TransparentWrapper for core::num::Wrapping {} +#[cfg(feature = "transparentwrapper_extra")] +#[cfg_attr( + feature = "nightly_docs", + doc(cfg(feature = "transparentwrapper_extra")) +)] +unsafe impl TransparentWrapper for core::num::Saturating {} + +// Note that `Reverse` existed since Rust 1.19.0, but was only made `#[repr(transparent)]` +// in Rust 1.52.0 (PR: https://github.com/rust-lang/rust/pull/81879), so we have it under +// the same feature as `Saturating`, which was stabilized in Rust 1.74.0, so that this +// impl cannot be used on a version before 1.52.0 where it would be unsound. +#[cfg(feature = "transparentwrapper_extra")] +#[cfg_attr( + feature = "nightly_docs", + doc(cfg(feature = "transparentwrapper_extra")) +)] +unsafe impl TransparentWrapper for core::cmp::Reverse {} diff --git a/third_party/rust/bytemuck/src/zeroable.rs b/third_party/rust/bytemuck/src/zeroable.rs new file mode 100644 index 000000000000..7e8992073d23 --- /dev/null +++ b/third_party/rust/bytemuck/src/zeroable.rs @@ -0,0 +1,263 @@ +use super::*; + +/// Trait for types that can be safely created with +/// [`zeroed`](core::mem::zeroed). +/// +/// An all-zeroes value may or may not be the same value as the +/// [Default](core::default::Default) value of the type. +/// +/// ## Safety +/// +/// * Your type must be inhabited (eg: no +/// [Infallible](core::convert::Infallible)). +/// * Your type must be allowed to be an "all zeroes" bit pattern (eg: no +/// [`NonNull`](core::ptr::NonNull)). +/// +/// ## Features +/// +/// Some `impl`s are feature gated due to the MSRV policy: +/// +/// * `MaybeUninit` was not available in 1.34.0, but is available under the +/// `zeroable_maybe_uninit` feature flag. +/// * `Atomic*` types require Rust 1.60.0 or later to work on certain platforms, +/// but is available under the `zeroable_atomics` feature flag. +/// * `[T; N]` for arbitrary `N` requires the `min_const_generics` feature flag. +pub unsafe trait Zeroable: Sized { + /// Calls [`zeroed`](core::mem::zeroed). + /// + /// This is a trait method so that you can write `MyType::zeroed()` in your + /// code. It is a contract of this trait that if you implement it on your type + /// you **must not** override this method. + #[inline] + fn zeroed() -> Self { + unsafe { core::mem::zeroed() } + } +} +unsafe impl Zeroable for () {} +unsafe impl Zeroable for bool {} +unsafe impl Zeroable for char {} +unsafe impl Zeroable for u8 {} +unsafe impl Zeroable for i8 {} +unsafe impl Zeroable for u16 {} +unsafe impl Zeroable for i16 {} +unsafe impl Zeroable for u32 {} +unsafe impl Zeroable for i32 {} +unsafe impl Zeroable for u64 {} +unsafe impl Zeroable for i64 {} +unsafe impl Zeroable for usize {} +unsafe impl Zeroable for isize {} +unsafe impl Zeroable for u128 {} +unsafe impl Zeroable for i128 {} +#[cfg(feature = "nightly_float")] +unsafe impl Zeroable for f16 {} +unsafe impl Zeroable for f32 {} +unsafe impl Zeroable for f64 {} +#[cfg(feature = "nightly_float")] +unsafe impl Zeroable for f128 {} +unsafe impl Zeroable for Wrapping {} +unsafe impl Zeroable for core::cmp::Reverse {} +#[cfg(feature = "pod_saturating")] +unsafe impl Zeroable for core::num::Saturating {} + +// Note: we can't implement this for all `T: ?Sized` types because it would +// create NULL pointers for vtables. +// Maybe one day this could be changed to be implemented for +// `T: ?Sized where ::Metadata: Zeroable`. +unsafe impl Zeroable for *mut T {} +unsafe impl Zeroable for *const T {} +unsafe impl Zeroable for *mut [T] {} +unsafe impl Zeroable for *const [T] {} +unsafe impl Zeroable for *mut str {} +unsafe impl Zeroable for *const str {} + +unsafe impl Zeroable for PhantomData {} +unsafe impl Zeroable for PhantomPinned {} +unsafe impl Zeroable for core::mem::ManuallyDrop {} +unsafe impl Zeroable for core::cell::UnsafeCell {} +unsafe impl Zeroable for core::cell::Cell {} + +#[cfg(feature = "zeroable_atomics")] +#[cfg_attr(feature = "nightly_docs", doc(cfg(feature = "zeroable_atomics")))] +mod atomic_impls { + use super::Zeroable; + + #[cfg(target_has_atomic = "8")] + unsafe impl Zeroable for core::sync::atomic::AtomicBool {} + #[cfg(target_has_atomic = "8")] + unsafe impl Zeroable for core::sync::atomic::AtomicU8 {} + #[cfg(target_has_atomic = "8")] + unsafe impl Zeroable for core::sync::atomic::AtomicI8 {} + + #[cfg(target_has_atomic = "16")] + unsafe impl Zeroable for core::sync::atomic::AtomicU16 {} + #[cfg(target_has_atomic = "16")] + unsafe impl Zeroable for core::sync::atomic::AtomicI16 {} + + #[cfg(target_has_atomic = "32")] + unsafe impl Zeroable for core::sync::atomic::AtomicU32 {} + #[cfg(target_has_atomic = "32")] + unsafe impl Zeroable for core::sync::atomic::AtomicI32 {} + + #[cfg(target_has_atomic = "64")] + unsafe impl Zeroable for core::sync::atomic::AtomicU64 {} + #[cfg(target_has_atomic = "64")] + unsafe impl Zeroable for core::sync::atomic::AtomicI64 {} + + #[cfg(target_has_atomic = "ptr")] + unsafe impl Zeroable for core::sync::atomic::AtomicUsize {} + #[cfg(target_has_atomic = "ptr")] + unsafe impl Zeroable for core::sync::atomic::AtomicIsize {} + + #[cfg(target_has_atomic = "ptr")] + unsafe impl Zeroable for core::sync::atomic::AtomicPtr {} +} + +#[cfg(feature = "zeroable_maybe_uninit")] +#[cfg_attr( + feature = "nightly_docs", + doc(cfg(feature = "zeroable_maybe_uninit")) +)] +unsafe impl Zeroable for core::mem::MaybeUninit {} + +unsafe impl Zeroable for (A,) {} +unsafe impl Zeroable for (A, B) {} +unsafe impl Zeroable for (A, B, C) {} +unsafe impl Zeroable + for (A, B, C, D) +{ +} +unsafe impl + Zeroable for (A, B, C, D, E) +{ +} +unsafe impl< + A: Zeroable, + B: Zeroable, + C: Zeroable, + D: Zeroable, + E: Zeroable, + F: Zeroable, + > Zeroable for (A, B, C, D, E, F) +{ +} +unsafe impl< + A: Zeroable, + B: Zeroable, + C: Zeroable, + D: Zeroable, + E: Zeroable, + F: Zeroable, + G: Zeroable, + > Zeroable for (A, B, C, D, E, F, G) +{ +} +unsafe impl< + A: Zeroable, + B: Zeroable, + C: Zeroable, + D: Zeroable, + E: Zeroable, + F: Zeroable, + G: Zeroable, + H: Zeroable, + > Zeroable for (A, B, C, D, E, F, G, H) +{ +} + +#[cfg(feature = "min_const_generics")] +#[cfg_attr(feature = "nightly_docs", doc(cfg(feature = "min_const_generics")))] +unsafe impl Zeroable for [T; N] where T: Zeroable {} + +#[cfg(not(feature = "min_const_generics"))] +impl_unsafe_marker_for_array!( + Zeroable, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 48, 64, 96, 128, 256, + 512, 1024, 2048, 4096 +); + +impl_unsafe_marker_for_simd!( + #[cfg(all(target_arch = "wasm32", feature = "wasm_simd"))] + unsafe impl Zeroable for wasm32::{v128} +); + +impl_unsafe_marker_for_simd!( + #[cfg(all(target_arch = "aarch64", feature = "aarch64_simd"))] + unsafe impl Zeroable for aarch64::{ + float32x2_t, float32x2x2_t, float32x2x3_t, float32x2x4_t, float32x4_t, + float32x4x2_t, float32x4x3_t, float32x4x4_t, float64x1_t, float64x1x2_t, + float64x1x3_t, float64x1x4_t, float64x2_t, float64x2x2_t, float64x2x3_t, + float64x2x4_t, int16x4_t, int16x4x2_t, int16x4x3_t, int16x4x4_t, int16x8_t, + int16x8x2_t, int16x8x3_t, int16x8x4_t, int32x2_t, int32x2x2_t, int32x2x3_t, + int32x2x4_t, int32x4_t, int32x4x2_t, int32x4x3_t, int32x4x4_t, int64x1_t, + int64x1x2_t, int64x1x3_t, int64x1x4_t, int64x2_t, int64x2x2_t, int64x2x3_t, + int64x2x4_t, int8x16_t, int8x16x2_t, int8x16x3_t, int8x16x4_t, int8x8_t, + int8x8x2_t, int8x8x3_t, int8x8x4_t, poly16x4_t, poly16x4x2_t, poly16x4x3_t, + poly16x4x4_t, poly16x8_t, poly16x8x2_t, poly16x8x3_t, poly16x8x4_t, + poly64x1_t, poly64x1x2_t, poly64x1x3_t, poly64x1x4_t, poly64x2_t, + poly64x2x2_t, poly64x2x3_t, poly64x2x4_t, poly8x16_t, poly8x16x2_t, + poly8x16x3_t, poly8x16x4_t, poly8x8_t, poly8x8x2_t, poly8x8x3_t, poly8x8x4_t, + uint16x4_t, uint16x4x2_t, uint16x4x3_t, uint16x4x4_t, uint16x8_t, + uint16x8x2_t, uint16x8x3_t, uint16x8x4_t, uint32x2_t, uint32x2x2_t, + uint32x2x3_t, uint32x2x4_t, uint32x4_t, uint32x4x2_t, uint32x4x3_t, + uint32x4x4_t, uint64x1_t, uint64x1x2_t, uint64x1x3_t, uint64x1x4_t, + uint64x2_t, uint64x2x2_t, uint64x2x3_t, uint64x2x4_t, uint8x16_t, + uint8x16x2_t, uint8x16x3_t, uint8x16x4_t, uint8x8_t, uint8x8x2_t, + uint8x8x3_t, uint8x8x4_t, + } +); + +impl_unsafe_marker_for_simd!( + #[cfg(target_arch = "x86")] + unsafe impl Zeroable for x86::{ + __m128i, __m128, __m128d, + __m256i, __m256, __m256d, + } +); + +impl_unsafe_marker_for_simd!( + #[cfg(target_arch = "x86_64")] + unsafe impl Zeroable for x86_64::{ + __m128i, __m128, __m128d, + __m256i, __m256, __m256d, + } +); + +#[cfg(feature = "nightly_portable_simd")] +#[cfg_attr( + feature = "nightly_docs", + doc(cfg(feature = "nightly_portable_simd")) +)] +unsafe impl Zeroable for core::simd::Simd +where + T: core::simd::SimdElement + Zeroable, + core::simd::LaneCount: core::simd::SupportedLaneCount, +{ +} + +impl_unsafe_marker_for_simd!( + #[cfg(all(target_arch = "x86", any(feature = "nightly_stdsimd", feature = "avx512_simd")))] + unsafe impl Zeroable for x86::{ + __m512, __m512d, __m512i + } +); + +impl_unsafe_marker_for_simd!( + #[cfg(all(target_arch = "x86_64", any(feature = "nightly_stdsimd", feature = "avx512_simd")))] + unsafe impl Zeroable for x86_64::{ + __m512, __m512d, __m512i + } +); + +impl_unsafe_marker_for_simd!( + #[cfg(all(target_arch = "x86", feature = "nightly_stdsimd"))] + unsafe impl Zeroable for x86::{ + __m128bh, __m256bh, __m512bh + } +); + +impl_unsafe_marker_for_simd!( + #[cfg(all(target_arch = "x86_64", feature = "nightly_stdsimd"))] + unsafe impl Zeroable for x86_64::{ + __m128bh, __m256bh, __m512bh + } +); diff --git a/third_party/rust/bytemuck/src/zeroable_in_option.rs b/third_party/rust/bytemuck/src/zeroable_in_option.rs new file mode 100644 index 000000000000..13737f473025 --- /dev/null +++ b/third_party/rust/bytemuck/src/zeroable_in_option.rs @@ -0,0 +1,35 @@ +use super::*; + +// Note(Lokathor): This is the neat part!! +unsafe impl Zeroable for Option {} + +/// Trait for types which are [Zeroable](Zeroable) when wrapped in +/// [Option](core::option::Option). +/// +/// ## Safety +/// +/// * `Option` must uphold the same invariants as +/// [Zeroable](Zeroable). +pub unsafe trait ZeroableInOption: Sized {} + +unsafe impl ZeroableInOption for NonZeroI8 {} +unsafe impl ZeroableInOption for NonZeroI16 {} +unsafe impl ZeroableInOption for NonZeroI32 {} +unsafe impl ZeroableInOption for NonZeroI64 {} +unsafe impl ZeroableInOption for NonZeroI128 {} +unsafe impl ZeroableInOption for NonZeroIsize {} +unsafe impl ZeroableInOption for NonZeroU8 {} +unsafe impl ZeroableInOption for NonZeroU16 {} +unsafe impl ZeroableInOption for NonZeroU32 {} +unsafe impl ZeroableInOption for NonZeroU64 {} +unsafe impl ZeroableInOption for NonZeroU128 {} +unsafe impl ZeroableInOption for NonZeroUsize {} + +// Note: this does not create NULL vtable because we get `None` anyway. +unsafe impl ZeroableInOption for NonNull {} +unsafe impl ZeroableInOption for &'_ T {} +unsafe impl ZeroableInOption for &'_ mut T {} + +#[cfg(feature = "extern_crate_alloc")] +#[cfg_attr(feature = "nightly_docs", doc(cfg(feature = "extern_crate_alloc")))] +unsafe impl ZeroableInOption for alloc::boxed::Box {} diff --git a/third_party/rust/bytemuck/tests/array_tests.rs b/third_party/rust/bytemuck/tests/array_tests.rs new file mode 100644 index 000000000000..552de08f70d5 --- /dev/null +++ b/third_party/rust/bytemuck/tests/array_tests.rs @@ -0,0 +1,12 @@ +#[test] +pub fn test_cast_array() { + let x = [0u32, 1u32, 2u32]; + let _: [u16; 6] = bytemuck::cast(x); +} + +#[cfg(feature = "min_const_generics")] +#[test] +pub fn test_cast_long_array() { + let x = [0u32; 65]; + let _: [u16; 130] = bytemuck::cast(x); +} diff --git a/third_party/rust/bytemuck/tests/cast_slice_tests.rs b/third_party/rust/bytemuck/tests/cast_slice_tests.rs new file mode 100644 index 000000000000..8446276399d1 --- /dev/null +++ b/third_party/rust/bytemuck/tests/cast_slice_tests.rs @@ -0,0 +1,371 @@ +#![allow(clippy::unnecessary_cast)] +#![allow(clippy::manual_slice_size_calculation)] + +use core::mem::size_of; + +use bytemuck::*; + +#[test] +fn test_try_cast_slice() { + // some align4 data + let u32_slice: &[u32] = &[4, 5, 6]; + // the same data as align1 + let the_bytes: &[u8] = try_cast_slice(u32_slice).unwrap(); + + assert_eq!( + u32_slice.as_ptr() as *const u32 as usize, + the_bytes.as_ptr() as *const u8 as usize + ); + assert_eq!( + u32_slice.len() * size_of::(), + the_bytes.len() * size_of::() + ); + + // by taking one byte off the front, we're definitely mis-aligned for u32. + let mis_aligned_bytes = &the_bytes[1..]; + assert_eq!( + try_cast_slice::(mis_aligned_bytes), + Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) + ); + + // by taking one byte off the end, we're aligned but would have slop bytes for + // u32 + let the_bytes_len_minus1 = the_bytes.len() - 1; + let slop_bytes = &the_bytes[..the_bytes_len_minus1]; + assert_eq!( + try_cast_slice::(slop_bytes), + Err(PodCastError::OutputSliceWouldHaveSlop) + ); + + // if we don't mess with it we can up-alignment cast + try_cast_slice::(the_bytes).unwrap(); +} + +#[test] +fn test_try_cast_slice_mut() { + // some align4 data + let u32_slice: &mut [u32] = &mut [4, 5, 6]; + let u32_len = u32_slice.len(); + let u32_ptr = u32_slice.as_ptr(); + + // the same data as align1 + let the_bytes: &mut [u8] = try_cast_slice_mut(u32_slice).unwrap(); + let the_bytes_len = the_bytes.len(); + let the_bytes_ptr = the_bytes.as_ptr(); + + assert_eq!( + u32_ptr as *const u32 as usize, + the_bytes_ptr as *const u8 as usize + ); + assert_eq!(u32_len * size_of::(), the_bytes_len * size_of::()); + + // by taking one byte off the front, we're definitely mis-aligned for u32. + let mis_aligned_bytes = &mut the_bytes[1..]; + assert_eq!( + try_cast_slice_mut::(mis_aligned_bytes), + Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) + ); + + // by taking one byte off the end, we're aligned but would have slop bytes for + // u32 + let the_bytes_len_minus1 = the_bytes.len() - 1; + let slop_bytes = &mut the_bytes[..the_bytes_len_minus1]; + assert_eq!( + try_cast_slice_mut::(slop_bytes), + Err(PodCastError::OutputSliceWouldHaveSlop) + ); + + // if we don't mess with it we can up-alignment cast + try_cast_slice_mut::(the_bytes).unwrap(); +} + +#[test] +fn test_types() { + let _: i32 = cast(1.0_f32); + let _: &mut i32 = cast_mut(&mut 1.0_f32); + let _: &i32 = cast_ref(&1.0_f32); + let _: &[i32] = cast_slice(&[1.0_f32]); + let _: &mut [i32] = cast_slice_mut(&mut [1.0_f32]); + // + let _: Result = try_cast(1.0_f32); + let _: Result<&mut i32, PodCastError> = try_cast_mut(&mut 1.0_f32); + let _: Result<&i32, PodCastError> = try_cast_ref(&1.0_f32); + let _: Result<&[i32], PodCastError> = try_cast_slice(&[1.0_f32]); + let _: Result<&mut [i32], PodCastError> = try_cast_slice_mut(&mut [1.0_f32]); +} + +#[test] +fn test_bytes_of() { + assert_eq!(bytes_of(&0xaabbccdd_u32), &0xaabbccdd_u32.to_ne_bytes()); + assert_eq!( + bytes_of_mut(&mut 0xaabbccdd_u32), + &mut 0xaabbccdd_u32.to_ne_bytes() + ); + let mut a = 0xaabbccdd_u32; + let a_addr = &a as *const _ as usize; + // ensure addresses match. + assert_eq!(bytes_of(&a).as_ptr() as usize, a_addr); + assert_eq!(bytes_of_mut(&mut a).as_ptr() as usize, a_addr); +} + +#[test] +fn test_try_from_bytes() { + let u32s = [0xaabbccdd, 0x11223344_u32]; + let bytes = bytemuck::cast_slice::(&u32s); + assert_eq!(try_from_bytes::(&bytes[..4]), Ok(&u32s[0])); + assert_eq!( + try_from_bytes::(&bytes[..5]), + Err(PodCastError::SizeMismatch) + ); + assert_eq!( + try_from_bytes::(&bytes[..3]), + Err(PodCastError::SizeMismatch) + ); + assert_eq!( + try_from_bytes::(&bytes[1..5]), + Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) + ); +} + +#[test] +fn test_try_from_bytes_mut() { + let mut abcd = 0xaabbccdd; + let mut u32s = [abcd, 0x11223344_u32]; + let bytes = bytemuck::cast_slice_mut::(&mut u32s); + assert_eq!(try_from_bytes_mut::(&mut bytes[..4]), Ok(&mut abcd)); + assert_eq!(try_from_bytes_mut::(&mut bytes[..4]), Ok(&mut abcd)); + assert_eq!( + try_from_bytes_mut::(&mut bytes[..5]), + Err(PodCastError::SizeMismatch) + ); + assert_eq!( + try_from_bytes_mut::(&mut bytes[..3]), + Err(PodCastError::SizeMismatch) + ); + assert_eq!( + try_from_bytes::(&bytes[1..5]), + Err(PodCastError::TargetAlignmentGreaterAndInputNotAligned) + ); +} + +#[test] +fn test_from_bytes() { + let abcd = 0xaabbccdd_u32; + let aligned_bytes = bytemuck::bytes_of(&abcd); + assert_eq!(from_bytes::(aligned_bytes), &abcd); + assert!(core::ptr::eq(from_bytes(aligned_bytes), &abcd)); +} + +#[test] +fn test_from_bytes_mut() { + let mut a = 0xaabbccdd_u32; + let a_addr = &a as *const _ as usize; + let aligned_bytes = bytemuck::bytes_of_mut(&mut a); + assert_eq!(*from_bytes_mut::(aligned_bytes), 0xaabbccdd_u32); + assert_eq!( + from_bytes_mut::(aligned_bytes) as *const u32 as usize, + a_addr + ); +} + +// like #[should_panic], but can be a part of another test, instead of requiring +// it to be it's own test. +macro_rules! should_panic { + ($ex:expr) => { + assert!( + std::panic::catch_unwind(|| { + let _ = $ex; + }) + .is_err(), + concat!("should have panicked: `", stringify!($ex), "`") + ); + }; +} + +#[test] +fn test_panics() { + should_panic!(cast_slice::(&[1u8, 2u8])); + should_panic!(cast_slice_mut::(&mut [1u8, 2u8])); + should_panic!(from_bytes::(&[1u8, 2])); + should_panic!(from_bytes::(&[1u8, 2, 3, 4, 5])); + should_panic!(from_bytes_mut::(&mut [1u8, 2])); + should_panic!(from_bytes_mut::(&mut [1u8, 2, 3, 4, 5])); + // use cast_slice on some u32s to get some align>=4 bytes, so we can know + // we'll give from_bytes unaligned ones. + let aligned_bytes = bytemuck::cast_slice::(&[0, 0]); + should_panic!(from_bytes::(&aligned_bytes[1..5])); +} + +#[test] +fn test_zsts() { + #[derive(Debug, Clone, Copy)] + struct MyZst; + unsafe impl Zeroable for MyZst {} + unsafe impl Pod for MyZst {} + assert_eq!(42, cast_slice::<(), MyZst>(&[(); 42]).len()); + assert_eq!(42, cast_slice_mut::<(), MyZst>(&mut [(); 42]).len()); + assert_eq!(0, cast_slice::<(), u8>(&[(); 42]).len()); + assert_eq!(0, cast_slice_mut::<(), u8>(&mut [(); 42]).len()); + assert_eq!(0, cast_slice::(&[]).len()); + assert_eq!(0, cast_slice_mut::(&mut []).len()); + + assert_eq!( + PodCastError::OutputSliceWouldHaveSlop, + try_cast_slice::(&[42]).unwrap_err() + ); + + assert_eq!( + PodCastError::OutputSliceWouldHaveSlop, + try_cast_slice_mut::(&mut [42]).unwrap_err() + ); +} + +#[cfg(feature = "extern_crate_alloc")] +#[test] +fn test_boxed_slices() { + let boxed_u8_slice: Box<[u8]> = Box::new([0, 1, u8::MAX, i8::MAX as u8]); + let boxed_i8_slice: Box<[i8]> = cast_slice_box::(boxed_u8_slice); + assert_eq!(&*boxed_i8_slice, [0, 1, -1, i8::MAX]); + + let result: Result, (PodCastError, Box<[i8]>)> = + try_cast_slice_box(boxed_i8_slice); + let (error, boxed_i8_slice) = + result.expect_err("u16 and i8 have different alignment"); + assert_eq!(error, PodCastError::AlignmentMismatch); + + let result: Result<&[[i8; 3]], PodCastError> = + try_cast_slice(&*boxed_i8_slice); + let error = + result.expect_err("slice of [i8; 3] cannot be made from slice of 4 i8s"); + assert_eq!(error, PodCastError::OutputSliceWouldHaveSlop); + + let result: Result, (PodCastError, Box<[i8]>)> = + try_cast_slice_box(boxed_i8_slice); + let (error, boxed_i8_slice) = + result.expect_err("slice of [i8; 3] cannot be made from slice of 4 i8s"); + assert_eq!(error, PodCastError::OutputSliceWouldHaveSlop); + + let empty: Box<[()]> = cast_slice_box::(Box::new([])); + assert!(empty.is_empty()); + + let result: Result, (PodCastError, Box<[i8]>)> = + try_cast_slice_box(boxed_i8_slice); + let (error, boxed_i8_slice) = + result.expect_err("slice of ZST cannot be made from slice of 4 u8s"); + assert_eq!(error, PodCastError::OutputSliceWouldHaveSlop); + + drop(boxed_i8_slice); + + let empty: Box<[i8]> = cast_slice_box::<(), i8>(Box::new([])); + assert!(empty.is_empty()); + + let empty: Box<[i8]> = cast_slice_box::<(), i8>(Box::new([(); 42])); + assert!(empty.is_empty()); +} + +#[cfg(feature = "extern_crate_alloc")] +#[test] +fn test_rc_slices() { + use std::rc::Rc; + let rc_u8_slice: Rc<[u8]> = Rc::new([0, 1, u8::MAX, i8::MAX as u8]); + let rc_i8_slice: Rc<[i8]> = cast_slice_rc::(rc_u8_slice); + assert_eq!(&*rc_i8_slice, [0, 1, -1, i8::MAX]); + + let result: Result, (PodCastError, Rc<[i8]>)> = + try_cast_slice_rc(rc_i8_slice); + let (error, rc_i8_slice) = + result.expect_err("u16 and i8 have different alignment"); + assert_eq!(error, PodCastError::AlignmentMismatch); + + let result: Result<&[[i8; 3]], PodCastError> = try_cast_slice(&*rc_i8_slice); + let error = + result.expect_err("slice of [i8; 3] cannot be made from slice of 4 i8s"); + assert_eq!(error, PodCastError::OutputSliceWouldHaveSlop); + + let result: Result, (PodCastError, Rc<[i8]>)> = + try_cast_slice_rc(rc_i8_slice); + let (error, rc_i8_slice) = + result.expect_err("slice of [i8; 3] cannot be made from slice of 4 i8s"); + assert_eq!(error, PodCastError::OutputSliceWouldHaveSlop); + + let empty: Rc<[()]> = cast_slice_rc::(Rc::new([])); + assert!(empty.is_empty()); + + let result: Result, (PodCastError, Rc<[i8]>)> = + try_cast_slice_rc(rc_i8_slice); + let (error, rc_i8_slice) = + result.expect_err("slice of ZST cannot be made from slice of 4 u8s"); + assert_eq!(error, PodCastError::OutputSliceWouldHaveSlop); + + drop(rc_i8_slice); + + let empty: Rc<[i8]> = cast_slice_rc::<(), i8>(Rc::new([])); + assert!(empty.is_empty()); + + let empty: Rc<[i8]> = cast_slice_rc::<(), i8>(Rc::new([(); 42])); + assert!(empty.is_empty()); +} + +#[cfg(feature = "extern_crate_alloc")] +#[cfg(target_has_atomic = "ptr")] +#[test] +fn test_arc_slices() { + use std::sync::Arc; + let arc_u8_slice: Arc<[u8]> = Arc::new([0, 1, u8::MAX, i8::MAX as u8]); + let arc_i8_slice: Arc<[i8]> = cast_slice_arc::(arc_u8_slice); + assert_eq!(&*arc_i8_slice, [0, 1, -1, i8::MAX]); + + let result: Result, (PodCastError, Arc<[i8]>)> = + try_cast_slice_arc(arc_i8_slice); + let (error, arc_i8_slice) = + result.expect_err("u16 and i8 have different alignment"); + assert_eq!(error, PodCastError::AlignmentMismatch); + + let result: Result<&[[i8; 3]], PodCastError> = try_cast_slice(&*arc_i8_slice); + let error = + result.expect_err("slice of [i8; 3] cannot be made from slice of 4 i8s"); + assert_eq!(error, PodCastError::OutputSliceWouldHaveSlop); + + let result: Result, (PodCastError, Arc<[i8]>)> = + try_cast_slice_arc(arc_i8_slice); + let (error, arc_i8_slice) = + result.expect_err("slice of [i8; 3] cannot be made from slice of 4 i8s"); + assert_eq!(error, PodCastError::OutputSliceWouldHaveSlop); + + let empty: Arc<[()]> = cast_slice_arc::(Arc::new([])); + assert!(empty.is_empty()); + + let result: Result, (PodCastError, Arc<[i8]>)> = + try_cast_slice_arc(arc_i8_slice); + let (error, arc_i8_slice) = + result.expect_err("slice of ZST cannot be made from slice of 4 u8s"); + assert_eq!(error, PodCastError::OutputSliceWouldHaveSlop); + + drop(arc_i8_slice); + + let empty: Arc<[i8]> = cast_slice_arc::<(), i8>(Arc::new([])); + assert!(empty.is_empty()); + + let empty: Arc<[i8]> = cast_slice_arc::<(), i8>(Arc::new([(); 42])); + assert!(empty.is_empty()); +} + +#[cfg(feature = "extern_crate_alloc")] +#[test] +fn box_bytes_zst() { + let x: BoxBytes = box_bytes_of(Box::new([0u8; 0])); + let _: Box<[u8]> = from_box_bytes(x); + + let x: BoxBytes = box_bytes_of(Box::new([0u8; 0])); + let _: Box<[()]> = from_box_bytes(x); + + let x: BoxBytes = box_bytes_of(Box::new([(); 0])); + let _: Box<[u8]> = from_box_bytes(x); + + let x: BoxBytes = box_bytes_of(Box::new([0u8])); + let res: Result, _> = try_from_box_bytes(x); + assert_eq!(res.unwrap_err().0, PodCastError::OutputSliceWouldHaveSlop); + + // regression test for dropping zero-sized BoxBytes + let _: BoxBytes = box_bytes_of(Box::new([0u8; 0])); +} diff --git a/third_party/rust/bytemuck/tests/checked_tests.rs b/third_party/rust/bytemuck/tests/checked_tests.rs new file mode 100644 index 000000000000..4b1490980c82 --- /dev/null +++ b/third_party/rust/bytemuck/tests/checked_tests.rs @@ -0,0 +1,419 @@ +#![allow(clippy::unnecessary_cast)] +#![allow(clippy::manual_slice_size_calculation)] + +use core::{ + mem::size_of, + num::{NonZeroU32, NonZeroU8}, +}; + +use bytemuck::{checked::CheckedCastError, *}; + +#[test] +fn test_try_cast_slice() { + // some align4 data + let nonzero_u32_slice: &[NonZeroU32] = &[ + NonZeroU32::new(4).unwrap(), + NonZeroU32::new(5).unwrap(), + NonZeroU32::new(6).unwrap(), + ]; + + // contains bytes with invalid bitpattern for NonZeroU8 + assert_eq!( + checked::try_cast_slice::(nonzero_u32_slice), + Err(CheckedCastError::InvalidBitPattern) + ); + + // the same data as align1 + let the_bytes: &[u8] = checked::try_cast_slice(nonzero_u32_slice).unwrap(); + + assert_eq!( + nonzero_u32_slice.as_ptr() as *const NonZeroU32 as usize, + the_bytes.as_ptr() as *const u8 as usize + ); + assert_eq!( + nonzero_u32_slice.len() * size_of::(), + the_bytes.len() * size_of::() + ); + + // by taking one byte off the front, we're definitely mis-aligned for + // NonZeroU32. + let mis_aligned_bytes = &the_bytes[1..]; + assert_eq!( + checked::try_cast_slice::(mis_aligned_bytes), + Err(CheckedCastError::PodCastError( + PodCastError::TargetAlignmentGreaterAndInputNotAligned + )) + ); + + // by taking one byte off the end, we're aligned but would have slop bytes for + // NonZeroU32 + let the_bytes_len_minus1 = the_bytes.len() - 1; + let slop_bytes = &the_bytes[..the_bytes_len_minus1]; + assert_eq!( + checked::try_cast_slice::(slop_bytes), + Err(CheckedCastError::PodCastError(PodCastError::OutputSliceWouldHaveSlop)) + ); + + // if we don't mess with it we can up-alignment cast + checked::try_cast_slice::(the_bytes).unwrap(); +} + +#[test] +fn test_try_cast_slice_mut() { + // some align4 data + let u32_slice: &mut [u32] = &mut [4, 5, 6]; + + // contains bytes with invalid bitpattern for NonZeroU8 + assert_eq!( + checked::try_cast_slice_mut::(u32_slice), + Err(CheckedCastError::InvalidBitPattern) + ); + + // some align4 data + let u32_slice: &mut [u32] = &mut [0x4444_4444, 0x5555_5555, 0x6666_6666]; + let u32_len = u32_slice.len(); + let u32_ptr = u32_slice.as_ptr(); + + // the same data as align1, nonzero bytes + let the_nonzero_bytes: &mut [NonZeroU8] = + checked::try_cast_slice_mut(u32_slice).unwrap(); + let the_nonzero_bytes_len = the_nonzero_bytes.len(); + let the_nonzero_bytes_ptr = the_nonzero_bytes.as_ptr(); + + assert_eq!( + u32_ptr as *const u32 as usize, + the_nonzero_bytes_ptr as *const NonZeroU8 as usize + ); + assert_eq!( + u32_len * size_of::(), + the_nonzero_bytes_len * size_of::() + ); + + // the same data as align1 + let the_bytes: &mut [u8] = checked::try_cast_slice_mut(u32_slice).unwrap(); + let the_bytes_len = the_bytes.len(); + let the_bytes_ptr = the_bytes.as_ptr(); + + assert_eq!( + u32_ptr as *const u32 as usize, + the_bytes_ptr as *const u8 as usize + ); + assert_eq!( + u32_len * size_of::(), + the_bytes_len * size_of::() + ); + + // by taking one byte off the front, we're definitely mis-aligned for u32. + let mis_aligned_bytes = &mut the_bytes[1..]; + assert_eq!( + checked::try_cast_slice_mut::(mis_aligned_bytes), + Err(CheckedCastError::PodCastError( + PodCastError::TargetAlignmentGreaterAndInputNotAligned + )) + ); + + // by taking one byte off the end, we're aligned but would have slop bytes for + // NonZeroU32 + let the_bytes_len_minus1 = the_bytes.len() - 1; + let slop_bytes = &mut the_bytes[..the_bytes_len_minus1]; + assert_eq!( + checked::try_cast_slice_mut::(slop_bytes), + Err(CheckedCastError::PodCastError(PodCastError::OutputSliceWouldHaveSlop)) + ); + + // if we don't mess with it we can up-alignment cast, since there are no + // zeroes in the original slice + checked::try_cast_slice_mut::(the_bytes).unwrap(); +} + +#[test] +fn test_types() { + let _: NonZeroU32 = checked::cast(1.0_f32); + let _: &mut NonZeroU32 = checked::cast_mut(&mut 1.0_f32); + let _: &NonZeroU32 = checked::cast_ref(&1.0_f32); + let _: &[NonZeroU32] = checked::cast_slice(&[1.0_f32]); + let _: &mut [NonZeroU32] = checked::cast_slice_mut(&mut [1.0_f32]); + // + let _: Result = checked::try_cast(1.0_f32); + let _: Result<&mut NonZeroU32, CheckedCastError> = + checked::try_cast_mut(&mut 1.0_f32); + let _: Result<&NonZeroU32, CheckedCastError> = + checked::try_cast_ref(&1.0_f32); + let _: Result<&[NonZeroU32], CheckedCastError> = + checked::try_cast_slice(&[1.0_f32]); + let _: Result<&mut [NonZeroU32], CheckedCastError> = + checked::try_cast_slice_mut(&mut [1.0_f32]); +} + +#[test] +fn test_try_pod_read_unaligned() { + let u32s = [0xaabbccdd, 0x11223344_u32]; + let bytes = bytemuck::checked::cast_slice::(&u32s); + + #[cfg(target_endian = "big")] + assert_eq!( + checked::try_pod_read_unaligned::(&bytes[1..5]), + Ok(NonZeroU32::new(0xbbccdd11).unwrap()) + ); + #[cfg(target_endian = "little")] + assert_eq!( + checked::try_pod_read_unaligned::(&bytes[1..5]), + Ok(NonZeroU32::new(0x44aabbcc).unwrap()) + ); + + let u32s = [0; 2]; + let bytes = bytemuck::checked::cast_slice::(&u32s); + + assert_eq!( + checked::try_pod_read_unaligned::(&bytes[1..5]), + Err(CheckedCastError::InvalidBitPattern) + ); +} + +#[test] +fn test_try_from_bytes() { + let nonzero_u32s = [ + NonZeroU32::new(0xaabbccdd).unwrap(), + NonZeroU32::new(0x11223344).unwrap(), + ]; + let bytes = bytemuck::checked::cast_slice::(&nonzero_u32s); + assert_eq!( + checked::try_from_bytes::(&bytes[..4]), + Ok(&nonzero_u32s[0]) + ); + assert_eq!( + checked::try_from_bytes::(&bytes[..5]), + Err(CheckedCastError::PodCastError(PodCastError::SizeMismatch)) + ); + assert_eq!( + checked::try_from_bytes::(&bytes[..3]), + Err(CheckedCastError::PodCastError(PodCastError::SizeMismatch)) + ); + assert_eq!( + checked::try_from_bytes::(&bytes[1..5]), + Err(CheckedCastError::PodCastError( + PodCastError::TargetAlignmentGreaterAndInputNotAligned + )) + ); + + let zero_u32s = [0, 0x11223344_u32]; + let bytes = bytemuck::checked::cast_slice::(&zero_u32s); + assert_eq!( + checked::try_from_bytes::(&bytes[..4]), + Err(CheckedCastError::InvalidBitPattern) + ); + assert_eq!( + checked::try_from_bytes::(&bytes[4..]), + Ok(&NonZeroU32::new(zero_u32s[1]).unwrap()) + ); + assert_eq!( + checked::try_from_bytes::(&bytes[..5]), + Err(CheckedCastError::PodCastError(PodCastError::SizeMismatch)) + ); + assert_eq!( + checked::try_from_bytes::(&bytes[..3]), + Err(CheckedCastError::PodCastError(PodCastError::SizeMismatch)) + ); + assert_eq!( + checked::try_from_bytes::(&bytes[1..5]), + Err(CheckedCastError::PodCastError( + PodCastError::TargetAlignmentGreaterAndInputNotAligned + )) + ); +} + +#[test] +fn test_try_from_bytes_mut() { + let a = 0xaabbccdd_u32; + let b = 0x11223344_u32; + let mut u32s = [a, b]; + let bytes = bytemuck::checked::cast_slice_mut::(&mut u32s); + assert_eq!( + checked::try_from_bytes_mut::(&mut bytes[..4]), + Ok(&mut NonZeroU32::new(a).unwrap()) + ); + assert_eq!( + checked::try_from_bytes_mut::(&mut bytes[4..]), + Ok(&mut NonZeroU32::new(b).unwrap()) + ); + assert_eq!( + checked::try_from_bytes_mut::(&mut bytes[..5]), + Err(CheckedCastError::PodCastError(PodCastError::SizeMismatch)) + ); + assert_eq!( + checked::try_from_bytes_mut::(&mut bytes[..3]), + Err(CheckedCastError::PodCastError(PodCastError::SizeMismatch)) + ); + assert_eq!( + checked::try_from_bytes::(&bytes[1..5]), + Err(CheckedCastError::PodCastError( + PodCastError::TargetAlignmentGreaterAndInputNotAligned + )) + ); + + let mut u32s = [0, b]; + let bytes = bytemuck::checked::cast_slice_mut::(&mut u32s); + assert_eq!( + checked::try_from_bytes_mut::(&mut bytes[..4]), + Err(CheckedCastError::InvalidBitPattern) + ); + assert_eq!( + checked::try_from_bytes_mut::(&mut bytes[4..]), + Ok(&mut NonZeroU32::new(b).unwrap()) + ); + assert_eq!( + checked::try_from_bytes_mut::(&mut bytes[..5]), + Err(CheckedCastError::PodCastError(PodCastError::SizeMismatch)) + ); + assert_eq!( + checked::try_from_bytes_mut::(&mut bytes[..3]), + Err(CheckedCastError::PodCastError(PodCastError::SizeMismatch)) + ); + assert_eq!( + checked::try_from_bytes::(&bytes[1..5]), + Err(CheckedCastError::PodCastError( + PodCastError::TargetAlignmentGreaterAndInputNotAligned + )) + ); +} + +#[test] +fn test_from_bytes() { + let abcd = 0xaabbccdd_u32; + let aligned_bytes = bytemuck::bytes_of(&abcd); + assert_eq!( + checked::from_bytes::(aligned_bytes), + &NonZeroU32::new(abcd).unwrap() + ); + assert!(core::ptr::eq( + checked::from_bytes(aligned_bytes) as *const NonZeroU32 as *const u32, + &abcd + )); +} + +#[test] +fn test_from_bytes_mut() { + let mut a = 0xaabbccdd_u32; + let a_addr = &a as *const _ as usize; + let aligned_bytes = bytemuck::bytes_of_mut(&mut a); + assert_eq!( + *checked::from_bytes_mut::(aligned_bytes), + NonZeroU32::new(0xaabbccdd).unwrap() + ); + assert_eq!( + checked::from_bytes_mut::(aligned_bytes) as *const NonZeroU32 + as usize, + a_addr + ); +} + +// like #[should_panic], but can be a part of another test, instead of requiring +// it to be it's own test. +macro_rules! should_panic { + ($ex:expr) => { + assert!( + std::panic::catch_unwind(|| { + let _ = $ex; + }) + .is_err(), + concat!("should have panicked: `", stringify!($ex), "`") + ); + }; +} + +#[test] +fn test_panics() { + should_panic!(checked::cast::(0)); + should_panic!(checked::cast_ref::(&0)); + should_panic!(checked::cast_mut::(&mut 0)); + should_panic!(checked::cast_slice::(&[1u8, 2u8])); + should_panic!(checked::cast_slice_mut::(&mut [1u8, 2u8])); + should_panic!(checked::from_bytes::(&[1u8, 2])); + should_panic!(checked::from_bytes::(&[1u8, 2, 3, 4, 5])); + should_panic!(checked::from_bytes_mut::(&mut [1u8, 2])); + should_panic!(checked::from_bytes_mut::(&mut [1u8, 2, 3, 4, 5])); + // use cast_slice on some u32s to get some align>=4 bytes, so we can know + // we'll give from_bytes unaligned ones. + let aligned_bytes = bytemuck::cast_slice::(&[0, 0]); + should_panic!(checked::from_bytes::(aligned_bytes)); + should_panic!(checked::from_bytes::(&aligned_bytes[1..5])); + should_panic!(checked::pod_read_unaligned::( + &aligned_bytes[1..5] + )); +} + +#[test] +fn test_char() { + assert_eq!(checked::try_cast::(0), Ok('\0')); + assert_eq!(checked::try_cast::(0xd7ff), Ok('\u{d7ff}')); + assert_eq!( + checked::try_cast::(0xd800), + Err(CheckedCastError::InvalidBitPattern) + ); + assert_eq!( + checked::try_cast::(0xdfff), + Err(CheckedCastError::InvalidBitPattern) + ); + assert_eq!(checked::try_cast::(0xe000), Ok('\u{e000}')); + assert_eq!(checked::try_cast::(0x10ffff), Ok('\u{10ffff}')); + assert_eq!( + checked::try_cast::(0x110000), + Err(CheckedCastError::InvalidBitPattern) + ); + assert_eq!( + checked::try_cast::(-1i32 as u32), + Err(CheckedCastError::InvalidBitPattern) + ); +} + +#[test] +fn test_bool() { + assert_eq!(checked::try_cast::(0), Ok(false)); + assert_eq!(checked::try_cast::(1), Ok(true)); + for i in 2..=255 { + assert_eq!( + checked::try_cast::(i), + Err(CheckedCastError::InvalidBitPattern) + ); + } + + assert_eq!(checked::try_from_bytes::(&[1]), Ok(&true)); + assert_eq!( + checked::try_from_bytes::(&[3]), + Err(CheckedCastError::InvalidBitPattern) + ); + assert_eq!( + checked::try_from_bytes::(&[0, 1]), + Err(CheckedCastError::PodCastError(PodCastError::SizeMismatch)) + ); +} + +#[test] +fn test_all_nonzero() { + use core::num::*; + macro_rules! test_nonzero { + ($nonzero:ty: $primitive:ty) => { + assert_eq!( + checked::try_cast::<$primitive, $nonzero>(0), + Err(CheckedCastError::InvalidBitPattern) + ); + assert_eq!( + checked::try_cast::<$primitive, $nonzero>(1), + Ok(<$nonzero>::new(1).unwrap()) + ); + }; + } + + test_nonzero!(NonZeroU8: u8); + test_nonzero!(NonZeroI8: i8); + test_nonzero!(NonZeroU16: u16); + test_nonzero!(NonZeroI16: i16); + test_nonzero!(NonZeroU32: u32); + test_nonzero!(NonZeroI32: i32); + test_nonzero!(NonZeroU64: u64); + test_nonzero!(NonZeroI64: i64); + test_nonzero!(NonZeroU128: u128); + test_nonzero!(NonZeroI128: i128); + test_nonzero!(NonZeroUsize: usize); + test_nonzero!(NonZeroIsize: isize); +} diff --git a/third_party/rust/bytemuck/tests/derive.rs b/third_party/rust/bytemuck/tests/derive.rs new file mode 100644 index 000000000000..c7da6cc02dd5 --- /dev/null +++ b/third_party/rust/bytemuck/tests/derive.rs @@ -0,0 +1,77 @@ +#![cfg(feature = "derive")] +#![allow(dead_code)] + +use bytemuck::{ByteEq, ByteHash, Pod, TransparentWrapper, Zeroable}; +use std::marker::PhantomData; + +#[derive(Copy, Clone, Pod, Zeroable, ByteEq, ByteHash)] +#[repr(C)] +struct Test { + a: u16, + b: u16, +} + +#[derive(TransparentWrapper)] +#[repr(transparent)] +struct TransparentSingle { + a: u16, +} + +#[derive(TransparentWrapper)] +#[repr(transparent)] +#[transparent(u16)] +struct TransparentWithZeroSized { + a: u16, + b: (), +} + +#[derive(TransparentWrapper)] +#[repr(transparent)] +struct TransparentWithGeneric { + a: T, +} + +/// Ensuring that no additional bounds are emitted. +/// See https://github.com/Lokathor/bytemuck/issues/145 +fn test_generic(x: T) -> TransparentWithGeneric { + TransparentWithGeneric::wrap(x) +} + +#[derive(TransparentWrapper)] +#[repr(transparent)] +#[transparent(T)] +struct TransparentWithGenericAndZeroSized { + a: (), + b: T, +} + +/// Ensuring that no additional bounds are emitted. +/// See https://github.com/Lokathor/bytemuck/issues/145 +fn test_generic_with_zst(x: T) -> TransparentWithGenericAndZeroSized { + TransparentWithGenericAndZeroSized::wrap(x) +} + +#[derive(TransparentWrapper)] +#[repr(transparent)] +struct TransparentUnsized { + a: dyn std::fmt::Debug, +} + +type DynDebug = dyn std::fmt::Debug; + +#[derive(TransparentWrapper)] +#[repr(transparent)] +#[transparent(DynDebug)] +struct TransparentUnsizedWithZeroSized { + a: (), + b: DynDebug, +} + +#[derive(TransparentWrapper)] +#[repr(transparent)] +#[transparent(DynDebug)] +struct TransparentUnsizedWithGenericZeroSizeds { + a: PhantomData, + b: PhantomData, + c: DynDebug, +} diff --git a/third_party/rust/bytemuck/tests/doc_tests.rs b/third_party/rust/bytemuck/tests/doc_tests.rs new file mode 100644 index 000000000000..cb0fb134a848 --- /dev/null +++ b/third_party/rust/bytemuck/tests/doc_tests.rs @@ -0,0 +1,124 @@ +#![allow(clippy::disallowed_names)] +#![allow(dead_code)] + +//! Cargo miri doesn't run doctests yet, so we duplicate these here. It's +//! probably not that important to sweat keeping these perfectly up to date, but +//! we should try to catch the cases where the primary tests are doctests. +use bytemuck::*; + +// Miri doesn't run on doctests, so... copypaste to the rescue. +#[test] +fn test_transparent_slice() { + #[repr(transparent)] + struct Slice([T]); + + unsafe impl TransparentWrapper<[T]> for Slice {} + + let s = Slice::wrap_ref(&[1u32, 2, 3]); + assert_eq!(&s.0, &[1, 2, 3]); + + let mut buf = [1, 2, 3u8]; + let _sm = Slice::wrap_mut(&mut buf); +} + +#[test] +fn test_transparent_basic() { + #[derive(Default)] + struct SomeStruct(u32); + + #[repr(transparent)] + struct MyWrapper(SomeStruct); + + unsafe impl TransparentWrapper for MyWrapper {} + + // interpret a reference to &SomeStruct as a &MyWrapper + let thing = SomeStruct::default(); + let wrapped_ref: &MyWrapper = MyWrapper::wrap_ref(&thing); + + // Works with &mut too. + let mut mut_thing = SomeStruct::default(); + let wrapped_mut: &mut MyWrapper = MyWrapper::wrap_mut(&mut mut_thing); + let _ = (wrapped_ref, wrapped_mut); +} + +// Work around miri not running doctests +#[test] +fn test_contiguous_doc() { + #[repr(u8)] + #[derive(Debug, Copy, Clone, PartialEq)] + enum Foo { + A = 0, + B = 1, + C = 2, + D = 3, + E = 4, + } + unsafe impl Contiguous for Foo { + type Int = u8; + const MIN_VALUE: u8 = Foo::A as u8; + const MAX_VALUE: u8 = Foo::E as u8; + } + + assert_eq!(Foo::from_integer(3).unwrap(), Foo::D); + assert_eq!(Foo::from_integer(8), None); + assert_eq!(Foo::C.into_integer(), 2); + assert_eq!(Foo::B.into_integer(), Foo::B as u8); +} + +#[test] +fn test_offsetof_vertex() { + #[repr(C)] + struct Vertex { + pos: [f32; 2], + uv: [u16; 2], + color: [u8; 4], + } + unsafe impl Zeroable for Vertex {} + + let pos = offset_of!(Zeroable::zeroed(), Vertex, pos); + let uv = offset_of!(Zeroable::zeroed(), Vertex, uv); + let color = offset_of!(Zeroable::zeroed(), Vertex, color); + + assert_eq!(pos, 0); + assert_eq!(uv, 8); + assert_eq!(color, 12); +} + +#[test] +fn test_offsetof_nonpod() { + #[derive(Default)] + struct Foo { + a: u8, + b: &'static str, + c: i32, + } + + let a_offset = offset_of!(Default::default(), Foo, a); + let b_offset = offset_of!(Default::default(), Foo, b); + let c_offset = offset_of!(Default::default(), Foo, c); + + assert_ne!(a_offset, b_offset); + assert_ne!(b_offset, c_offset); + // We can't check against hardcoded values for a repr(Rust) type, + // but prove to ourself this way. + + let foo = Foo::default(); + // Note: offsets are in bytes. + let as_bytes = &foo as *const _ as *const u8; + + // We're using wrapping_offset here because it's not worth + // the unsafe block, but it would be valid to use `add` instead, + // as it cannot overflow. + assert_eq!( + &foo.a as *const _ as usize, + as_bytes.wrapping_add(a_offset) as usize + ); + assert_eq!( + &foo.b as *const _ as usize, + as_bytes.wrapping_add(b_offset) as usize + ); + assert_eq!( + &foo.c as *const _ as usize, + as_bytes.wrapping_add(c_offset) as usize + ); +} diff --git a/third_party/rust/bytemuck/tests/offset_of_tests.rs b/third_party/rust/bytemuck/tests/offset_of_tests.rs new file mode 100644 index 000000000000..b462237b47e9 --- /dev/null +++ b/third_party/rust/bytemuck/tests/offset_of_tests.rs @@ -0,0 +1,60 @@ +#![allow(clippy::disallowed_names)] +use bytemuck::{offset_of, Zeroable}; + +#[test] +fn test_offset_of_vertex() { + #[repr(C)] + struct Vertex { + pos: [f32; 2], + uv: [u16; 2], + color: [u8; 4], + } + unsafe impl Zeroable for Vertex {} + + let pos = offset_of!(Zeroable::zeroed(), Vertex, pos); + let uv = offset_of!(Zeroable::zeroed(), Vertex, uv); + let color = offset_of!(Zeroable::zeroed(), Vertex, color); + + assert_eq!(pos, 0); + assert_eq!(uv, 8); + assert_eq!(color, 12); +} + +#[test] +fn test_offset_of_foo() { + #[derive(Default)] + struct Foo { + a: u8, + b: &'static str, + c: i32, + } + + let a_offset = offset_of!(Default::default(), Foo, a); + let b_offset = offset_of!(Default::default(), Foo, b); + let c_offset = offset_of!(Default::default(), Foo, c); + + assert_ne!(a_offset, b_offset); + assert_ne!(b_offset, c_offset); + // We can't check against hardcoded values for a repr(Rust) type, + // but prove to ourself this way. + + let foo = Foo::default(); + // Note: offsets are in bytes. + let as_bytes = &foo as *const _ as *const u8; + + // we're using wrapping_offset here because it's not worth + // the unsafe block, but it would be valid to use `add` instead, + // as it cannot overflow. + assert_eq!( + &foo.a as *const _ as usize, + as_bytes.wrapping_add(a_offset) as usize + ); + assert_eq!( + &foo.b as *const _ as usize, + as_bytes.wrapping_add(b_offset) as usize + ); + assert_eq!( + &foo.c as *const _ as usize, + as_bytes.wrapping_add(c_offset) as usize + ); +} diff --git a/third_party/rust/bytemuck/tests/std_tests.rs b/third_party/rust/bytemuck/tests/std_tests.rs new file mode 100644 index 000000000000..0949982e0735 --- /dev/null +++ b/third_party/rust/bytemuck/tests/std_tests.rs @@ -0,0 +1,107 @@ +#![allow(clippy::uninlined_format_args)] +#![allow(unused_imports)] +//! The integration tests seem to always have `std` linked, so things that would +//! depend on that can go here. + +use bytemuck::*; +use core::num::NonZeroU8; + +#[test] +fn test_transparent_vtabled() { + use core::fmt::Display; + + #[repr(transparent)] + struct DisplayTraitObj(dyn Display); + + unsafe impl TransparentWrapper for DisplayTraitObj {} + + impl Display for DisplayTraitObj { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + self.0.fmt(f) + } + } + + let v = DisplayTraitObj::wrap_ref(&5i32); + let s = format!("{}", v); + assert_eq!(s, "5"); + + let mut x = 100i32; + let v_mut = DisplayTraitObj::wrap_mut(&mut x); + let s = format!("{}", v_mut); + assert_eq!(s, "100"); +} + +#[test] +#[cfg(feature = "extern_crate_alloc")] +fn test_large_box_alloc() { + type SuperPage = [[u8; 4096]; 4096]; + let _: Box = try_zeroed_box().unwrap(); +} + +#[test] +#[cfg(feature = "extern_crate_alloc")] +fn test_zero_sized_box_alloc() { + #[repr(align(4096))] + struct Empty; + unsafe impl Zeroable for Empty {} + let _: Box = try_zeroed_box().unwrap(); +} + +#[test] +#[cfg(feature = "extern_crate_alloc")] +fn test_try_from_box_bytes() { + // Different layout: target alignment is greater than source alignment. + assert_eq!( + try_from_box_bytes::(Box::new([0u8; 4]).into()).map_err(|(x, _)| x), + Err(PodCastError::AlignmentMismatch) + ); + + // Different layout: target alignment is less than source alignment. + assert_eq!( + try_from_box_bytes::(Box::new(0u64).into()).map_err(|(x, _)| x), + Err(PodCastError::AlignmentMismatch) + ); + + // Different layout: target size is greater than source size. + assert_eq!( + try_from_box_bytes::<[u32; 2]>(Box::new(0u32).into()).map_err(|(x, _)| x), + Err(PodCastError::SizeMismatch) + ); + + // Different layout: target size is less than source size. + assert_eq!( + try_from_box_bytes::(Box::new([0u32; 2]).into()).map_err(|(x, _)| x), + Err(PodCastError::SizeMismatch) + ); + + // Round trip: alignment is equal to size. + assert_eq!(*from_box_bytes::(Box::new(1000u32).into()), 1000u32); + + // Round trip: alignment is divider of size. + assert_eq!(&*from_box_bytes::<[u8; 5]>(Box::new(*b"hello").into()), b"hello"); + + // It's ok for T to have uninitialized bytes. + #[cfg(feature = "derive")] + { + #[derive(Debug, Copy, Clone, PartialEq, Eq, AnyBitPattern)] + struct Foo(u8, u16); + assert_eq!( + *from_box_bytes::(Box::new([0xc5c5u16; 2]).into()), + Foo(0xc5u8, 0xc5c5u16) + ); + } +} + +#[test] +#[cfg(feature = "extern_crate_alloc")] +fn test_box_bytes_of() { + assert_eq!(&*box_bytes_of(Box::new(*b"hello")), b"hello"); + + #[cfg(target_endian = "big")] + assert_eq!(&*box_bytes_of(Box::new(0x12345678)), b"\x12\x34\x56\x78"); + #[cfg(target_endian = "little")] + assert_eq!(&*box_bytes_of(Box::new(0x12345678)), b"\x78\x56\x34\x12"); + + // It's ok for T to have invalid bit patterns. + assert_eq!(&*box_bytes_of(Box::new(NonZeroU8::new(0xc5))), b"\xc5"); +} diff --git a/third_party/rust/bytemuck/tests/transparent.rs b/third_party/rust/bytemuck/tests/transparent.rs new file mode 100644 index 000000000000..ad34d9220e5f --- /dev/null +++ b/third_party/rust/bytemuck/tests/transparent.rs @@ -0,0 +1,116 @@ +// Currently this test doesn't actually check the output of the functions. +// It's only here for miri to check for any potential undefined behaviour. +// TODO: check function results + +#[test] +fn test_transparent_wrapper() { + // An external type defined in a different crate. + #[derive(Debug, Copy, Clone, Default)] + struct Foreign(u8); + + use bytemuck::TransparentWrapper; + + #[derive(Debug, Copy, Clone)] + #[repr(transparent)] + struct Wrapper(Foreign); + + unsafe impl TransparentWrapper for Wrapper {} + + // Traits can be implemented on crate-local wrapper. + unsafe impl bytemuck::Zeroable for Wrapper {} + unsafe impl bytemuck::Pod for Wrapper {} + + impl PartialEq for Foreign { + fn eq(&self, &other: &u8) -> bool { + self.0 == other + } + } + + impl PartialEq for Wrapper { + fn eq(&self, &other: &u8) -> bool { + self.0 == other + } + } + + let _: u8 = bytemuck::cast(Wrapper::wrap(Foreign::default())); + let _: Foreign = Wrapper::peel(bytemuck::cast(u8::default())); + + let _: &u8 = bytemuck::cast_ref(Wrapper::wrap_ref(&Foreign::default())); + let _: &Foreign = Wrapper::peel_ref(bytemuck::cast_ref(&u8::default())); + + let _: &mut u8 = + bytemuck::cast_mut(Wrapper::wrap_mut(&mut Foreign::default())); + let _: &mut Foreign = + Wrapper::peel_mut(bytemuck::cast_mut(&mut u8::default())); + + let _: &[u8] = + bytemuck::cast_slice(Wrapper::wrap_slice(&[Foreign::default()])); + let _: &[Foreign] = + Wrapper::peel_slice(bytemuck::cast_slice(&[u8::default()])); + + let _: &mut [u8] = + bytemuck::cast_slice_mut(Wrapper::wrap_slice_mut( + &mut [Foreign::default()], + )); + let _: &mut [Foreign] = + Wrapper::peel_slice_mut(bytemuck::cast_slice_mut(&mut [u8::default()])); + + let _: &[u8] = bytemuck::bytes_of(Wrapper::wrap_ref(&Foreign::default())); + let _: &Foreign = Wrapper::peel_ref(bytemuck::from_bytes(&[u8::default()])); + + let _: &mut [u8] = + bytemuck::bytes_of_mut(Wrapper::wrap_mut(&mut Foreign::default())); + let _: &mut Foreign = + Wrapper::peel_mut(bytemuck::from_bytes_mut(&mut [u8::default()])); + + // not sure if this is the right usage + let _ = + bytemuck::pod_align_to::<_, u8>(Wrapper::wrap_slice(&[Foreign::default()])); + // counterpart? + + // not sure if this is the right usage + let _ = bytemuck::pod_align_to_mut::<_, u8>(Wrapper::wrap_slice_mut(&mut [ + Foreign::default(), + ])); + // counterpart? + + #[cfg(feature = "extern_crate_alloc")] + { + use bytemuck::allocation::TransparentWrapperAlloc; + use std::rc::Rc; + + let a: Vec = vec![Foreign::default(); 2]; + + let b: Vec = Wrapper::wrap_vec(a); + assert_eq!(b, [0, 0]); + + let c: Vec = Wrapper::peel_vec(b); + assert_eq!(c, [0, 0]); + + let d: Box = Box::new(Foreign::default()); + + let e: Box = Wrapper::wrap_box(d); + assert_eq!(&*e, &0); + let f: Box = Wrapper::peel_box(e); + assert_eq!(&*f, &0); + + let g: Rc = Rc::new(Foreign::default()); + + let h: Rc = Wrapper::wrap_rc(g); + assert_eq!(&*h, &0); + let i: Rc = Wrapper::peel_rc(h); + assert_eq!(&*i, &0); + + #[cfg(target_has_atomic = "ptr")] + { + use std::sync::Arc; + + let j: Arc = Arc::new(Foreign::default()); + + let k: Arc = Wrapper::wrap_arc(j); + assert_eq!(&*k, &0); + let l: Arc = Wrapper::peel_arc(k); + assert_eq!(&*l, &0); + } + } +} diff --git a/third_party/rust/bytemuck/tests/wrapper_forgets.rs b/third_party/rust/bytemuck/tests/wrapper_forgets.rs new file mode 100644 index 000000000000..da3404f38d18 --- /dev/null +++ b/third_party/rust/bytemuck/tests/wrapper_forgets.rs @@ -0,0 +1,13 @@ +use bytemuck::TransparentWrapper; + +#[repr(transparent)] +struct Wrap(Box); + +// SAFETY: it's #[repr(transparent)] +unsafe impl TransparentWrapper> for Wrap {} + +fn main() { + let value = Box::new(5); + // This used to duplicate the wrapped value, creating a double free :( + Wrap::wrap(value); +} diff --git a/third_party/rust/bytemuck_derive/.cargo-checksum.json b/third_party/rust/bytemuck_derive/.cargo-checksum.json new file mode 100644 index 000000000000..017340764bd8 --- /dev/null +++ b/third_party/rust/bytemuck_derive/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.lock":"39d577d4b970c0da55608df02d60c70222e196a68657bfbe71f0ed7b16e3d163","Cargo.toml":"7b1e2a5490f67f69d7421b3d17acb37d6c381a8045299e2c6244ebe032896c2e","LICENSE-APACHE":"e3ba223bb1423f0aad8c3dfce0fe3148db48926d41e6fbc3afbbf5ff9e1c89cb","LICENSE-MIT":"9df9ba60a11af705f2e451b53762686e615d86f76b169cf075c3237730dbd7e2","LICENSE-ZLIB":"84b34dd7608f7fb9b17bd588a6bf392bf7de504e2716f024a77d89f1b145a151","README.md":"09d8238fd7fdac39857da88e090667d8327ca9ac240768e216ef2079c2f06846","changelog.md":"8f02aa2556de3e9a691e0e081da4aabc74219f360e42425c3cc23659ae72a174","src/lib.rs":"1788629b806a5325b148d79aef683c4ff5aa08a76fd49a8a48d79c7c42d9f530","src/traits.rs":"0998ad93802f4619d72222acd1f3d3afe0172ed3e497b9a53ece90a5457176a5","tests/basic.rs":"35cf3dce742d7f6d52c811d1960c984ead98c4a3f2a3088c7a718c19e50d8347"},"package":"7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1"} \ No newline at end of file diff --git a/third_party/rust/bytemuck_derive/Cargo.lock b/third_party/rust/bytemuck_derive/Cargo.lock new file mode 100644 index 000000000000..5c1b60ad07b9 --- /dev/null +++ b/third_party/rust/bytemuck_derive/Cargo.lock @@ -0,0 +1,47 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bytemuck_derive" +version = "1.9.3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" diff --git a/third_party/rust/bytemuck_derive/Cargo.toml b/third_party/rust/bytemuck_derive/Cargo.toml new file mode 100644 index 000000000000..5455bccb257c --- /dev/null +++ b/third_party/rust/bytemuck_derive/Cargo.toml @@ -0,0 +1,56 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2018" +rust-version = "1.61" +name = "bytemuck_derive" +version = "1.9.3" +authors = ["Lokathor "] +build = false +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "derive proc-macros for `bytemuck`" +readme = "README.md" +keywords = [ + "transmute", + "bytes", + "casting", +] +categories = [ + "encoding", + "no-std", +] +license = "Zlib OR Apache-2.0 OR MIT" +repository = "https://github.com/Lokathor/bytemuck" + +[lib] +name = "bytemuck_derive" +path = "src/lib.rs" +proc-macro = true + +[[test]] +name = "basic" +path = "tests/basic.rs" + +[dependencies.proc-macro2] +version = "1.0.60" + +[dependencies.quote] +version = "1" + +[dependencies.syn] +version = "2.0.1" + +[dev-dependencies] diff --git a/third_party/rust/bytemuck_derive/LICENSE-APACHE b/third_party/rust/bytemuck_derive/LICENSE-APACHE new file mode 100644 index 000000000000..136d90045663 --- /dev/null +++ b/third_party/rust/bytemuck_derive/LICENSE-APACHE @@ -0,0 +1,61 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + + "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/third_party/rust/bytemuck_derive/LICENSE-MIT b/third_party/rust/bytemuck_derive/LICENSE-MIT new file mode 100644 index 000000000000..164045faadd4 --- /dev/null +++ b/third_party/rust/bytemuck_derive/LICENSE-MIT @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2019 Daniel "Lokathor" Gee. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/third_party/rust/bytemuck_derive/LICENSE-ZLIB b/third_party/rust/bytemuck_derive/LICENSE-ZLIB new file mode 100644 index 000000000000..d70707c75a24 --- /dev/null +++ b/third_party/rust/bytemuck_derive/LICENSE-ZLIB @@ -0,0 +1,11 @@ +Copyright (c) 2019 Daniel "Lokathor" Gee. + +This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. + +Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. + +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. + +3. This notice may not be removed or altered from any source distribution. diff --git a/third_party/rust/bytemuck_derive/README.md b/third_party/rust/bytemuck_derive/README.md new file mode 100644 index 000000000000..553a40ef97bd --- /dev/null +++ b/third_party/rust/bytemuck_derive/README.md @@ -0,0 +1,4 @@ + +# bytemuck_derive + +Derive macros for [bytemuck](https://docs.rs/bytemuck) traits. diff --git a/third_party/rust/bytemuck_derive/changelog.md b/third_party/rust/bytemuck_derive/changelog.md new file mode 100644 index 000000000000..a895f802f08c --- /dev/null +++ b/third_party/rust/bytemuck_derive/changelog.md @@ -0,0 +1,102 @@ + +## `bytemuck_derive` changelog + +## 1.9.2 + +* Removes the `resolver` key from the manifest. This breaks the build + with cryptic error messages despite current docs vaguely saying otherwise. + +## 1.9.1 + +* Fix the derive of CheckedBitPattern when used with a packed struct. + The Debug impl did not account for possibly-unaligned fields. + +## 1.9.0 + +* The crate now declares an MSRV, so updates going forward should be simpler. + +## 1.8.1 + +* https://github.com/Lokathor/bytemuck/pull/287 + +## 1.8 + +* [#257](https://github.com/Lokathor/bytemuck/pull/257): Allows deriving Zeroable on some enums. + +## 1.7.1 + +* Adds the `bytemuck` attribute to the `NoUninit` derive, allowing it to be used when re-exported. + +## 1.7.0 + +* Allow generics in `derive(ByteEq, ByteHash)` https://github.com/Lokathor/bytemuck/pull/219 + +## 1.6.0 + +* This allows `CheckedBitPattern` to be derived for enums with fields. + The repr must be one of the following: + * `#[repr(C)]` + * `#[repr(C, int)]` + * `#[repr(int)]` + * `#[repr(transparent)]` + +## 1.5.0 + +* The `Zeroable` derive now allows custom bounds. See the rustdoc for an explanation. + +## 1.4.1 + +* Move the `syn` dependency to use version 2. + This should not affect the public API in any way. + +## 1.4.0 + +* `ByteEq` and `ByteHash` derives will make `Eq` and `Hash` impls that treat the + value as a `&[u8]` during equality checks and hashing. This provides a large + codegen improvement for some types. +* Derives of `repr(int)` enums should now accept byte literal values as the + discriminant. + +## 1.3.0 + +* Allow `repr(transparent)` to be used generically in `derive(Pod)`. + +## 1.2.1 + +* Fixed a regression of the `align(N)` attribute that occurred during otherwise + routine cleanup. + +## 1.2.0 + +* Apparently our minimum required version of `syn` went up without anyone + noticing for a while. Because of a bump in our `syn` requirements, we're also + issuing this minor version bump in the `bytemuck_derive` crate. Because it's + possible to *reduce* the minimum required version of a dep in only a patch + release, I'm going to ratchet the required version of `syn` all the way up to + "current" (1.0.99). If absolutely necessary we could probably reduce the + minimum `syn` version again in a patch release for 1.2, but I don't want to + play this dance too much so I'd rather make each jump as big as can possibly + be. [Issue 122](https://github.com/Lokathor/bytemuck/issues/122). **Note:** + While the core `bytemuck` crate continues to keep building on rustc-1.34.0, + the `bytemuck_derive` crate is considered an opt-in bonus feature (which + doesn't do anything you couldn't trivially do yourself) and so it does not + support a specific MSRV. + +## 1.1.1 + +* Adjusted the license files to use full files rather than symlinks. + [PR](https://github.com/Lokathor/bytemuck/pull/118) + The license is unchanged, just no more symlinks. + +## 1.1.0 + +* Updated to work with `bytemuck-1.9.0` + +## 1.0.1 + +* [yanchith](https://github.com/yanchith) fixed the derive checks code to make clippy more happy. +[PR 45](https://github.com/Lokathor/bytemuck/pull/45) + +## 1.0.0 + +* Initial stable release. diff --git a/third_party/rust/bytemuck_derive/src/lib.rs b/third_party/rust/bytemuck_derive/src/lib.rs new file mode 100644 index 000000000000..26ecfc1c7d81 --- /dev/null +++ b/third_party/rust/bytemuck_derive/src/lib.rs @@ -0,0 +1,702 @@ +//! Derive macros for [bytemuck](https://docs.rs/bytemuck) traits. + +extern crate proc_macro; + +mod traits; + +use proc_macro2::TokenStream; +use quote::quote; +use syn::{parse_macro_input, DeriveInput, Result}; + +use crate::traits::{ + bytemuck_crate_name, AnyBitPattern, CheckedBitPattern, Contiguous, Derivable, + NoUninit, Pod, TransparentWrapper, Zeroable, +}; + +/// Derive the `Pod` trait for a struct +/// +/// The macro ensures that the struct follows all the the safety requirements +/// for the `Pod` trait. +/// +/// The following constraints need to be satisfied for the macro to succeed +/// +/// - All fields in the struct must implement `Pod` +/// - The struct must be `#[repr(C)]` or `#[repr(transparent)]` +/// - The struct must not contain any padding bytes +/// - The struct contains no generic parameters, if it is not +/// `#[repr(transparent)]` +/// +/// ## Examples +/// +/// ```rust +/// # use std::marker::PhantomData; +/// # use bytemuck_derive::{Pod, Zeroable}; +/// #[derive(Copy, Clone, Pod, Zeroable)] +/// #[repr(C)] +/// struct Test { +/// a: u16, +/// b: u16, +/// } +/// +/// #[derive(Copy, Clone, Pod, Zeroable)] +/// #[repr(transparent)] +/// struct Generic { +/// a: A, +/// b: PhantomData, +/// } +/// ``` +/// +/// If the struct is generic, it must be `#[repr(transparent)]` also. +/// +/// ```compile_fail +/// # use bytemuck::{Pod, Zeroable}; +/// # use std::marker::PhantomData; +/// #[derive(Copy, Clone, Pod, Zeroable)] +/// #[repr(C)] // must be `#[repr(transparent)]` +/// struct Generic
{ +/// a: A, +/// } +/// ``` +/// +/// If the struct is generic and `#[repr(transparent)]`, then it is only `Pod` +/// when all of its generics are `Pod`, not just its fields. +/// +/// ``` +/// # use bytemuck::{Pod, Zeroable}; +/// # use std::marker::PhantomData; +/// #[derive(Copy, Clone, Pod, Zeroable)] +/// #[repr(transparent)] +/// struct Generic { +/// a: A, +/// b: PhantomData, +/// } +/// +/// let _: u32 = bytemuck::cast(Generic { a: 4u32, b: PhantomData:: }); +/// ``` +/// +/// ```compile_fail +/// # use bytemuck::{Pod, Zeroable}; +/// # use std::marker::PhantomData; +/// # #[derive(Copy, Clone, Pod, Zeroable)] +/// # #[repr(transparent)] +/// # struct Generic { +/// # a: A, +/// # b: PhantomData, +/// # } +/// struct NotPod; +/// +/// let _: u32 = bytemuck::cast(Generic { a: 4u32, b: PhantomData:: }); +/// ``` +#[proc_macro_derive(Pod, attributes(bytemuck))] +pub fn derive_pod(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let expanded = + derive_marker_trait::(parse_macro_input!(input as DeriveInput)); + + proc_macro::TokenStream::from(expanded) +} + +/// Derive the `AnyBitPattern` trait for a struct +/// +/// The macro ensures that the struct follows all the the safety requirements +/// for the `AnyBitPattern` trait. +/// +/// The following constraints need to be satisfied for the macro to succeed +/// +/// - All fields in the struct must to implement `AnyBitPattern` +#[proc_macro_derive(AnyBitPattern, attributes(bytemuck))] +pub fn derive_anybitpattern( + input: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + let expanded = derive_marker_trait::(parse_macro_input!( + input as DeriveInput + )); + + proc_macro::TokenStream::from(expanded) +} + +/// Derive the `Zeroable` trait for a type. +/// +/// The macro ensures that the type follows all the the safety requirements +/// for the `Zeroable` trait. +/// +/// The following constraints need to be satisfied for the macro to succeed on a +/// struct: +/// +/// - All fields in the struct must implement `Zeroable` +/// +/// The following constraints need to be satisfied for the macro to succeed on +/// an enum: +/// +/// - The enum has an explicit `#[repr(Int)]`, `#[repr(C)]`, or `#[repr(C, +/// Int)]`. +/// - The enum has a variant with discriminant 0 (explicitly or implicitly). +/// - All fields in the variant with discriminant 0 (if any) must implement +/// `Zeroable` +/// +/// The macro always succeeds on unions. +/// +/// ## Example +/// +/// ```rust +/// # use bytemuck_derive::{Zeroable}; +/// #[derive(Copy, Clone, Zeroable)] +/// #[repr(C)] +/// struct Test { +/// a: u16, +/// b: u16, +/// } +/// ``` +/// ```rust +/// # use bytemuck_derive::{Zeroable}; +/// #[derive(Copy, Clone, Zeroable)] +/// #[repr(i32)] +/// enum Values { +/// A = 0, +/// B = 1, +/// C = 2, +/// } +/// #[derive(Clone, Zeroable)] +/// #[repr(C)] +/// enum Implicit { +/// A(bool, u8, char), +/// B(String), +/// C(std::num::NonZeroU8), +/// } +/// ``` +/// +/// # Custom bounds +/// +/// Custom bounds for the derived `Zeroable` impl can be given using the +/// `#[zeroable(bound = "")]` helper attribute. +/// +/// Using this attribute additionally opts-in to "perfect derive" semantics, +/// where instead of adding bounds for each generic type parameter, bounds are +/// added for each field's type. +/// +/// ## Examples +/// +/// ```rust +/// # use bytemuck::Zeroable; +/// # use std::marker::PhantomData; +/// #[derive(Clone, Zeroable)] +/// #[zeroable(bound = "")] +/// struct AlwaysZeroable { +/// a: PhantomData, +/// } +/// +/// AlwaysZeroable::::zeroed(); +/// ``` +/// ```rust +/// # use bytemuck::{Zeroable}; +/// #[derive(Copy, Clone, Zeroable)] +/// #[repr(u8)] +/// #[zeroable(bound = "")] +/// enum MyOption { +/// None, +/// Some(T), +/// } +/// +/// assert!(matches!(MyOption::::zeroed(), MyOption::None)); +/// ``` +/// +/// ```rust,compile_fail +/// # use bytemuck::Zeroable; +/// # use std::marker::PhantomData; +/// #[derive(Clone, Zeroable)] +/// #[zeroable(bound = "T: Copy")] +/// struct ZeroableWhenTIsCopy { +/// a: PhantomData, +/// } +/// +/// ZeroableWhenTIsCopy::::zeroed(); +/// ``` +/// +/// The restriction that all fields must be Zeroable is still applied, and this +/// is enforced using the mentioned "perfect derive" semantics. +/// +/// ```rust +/// # use bytemuck::Zeroable; +/// #[derive(Clone, Zeroable)] +/// #[zeroable(bound = "")] +/// struct ZeroableWhenTIsZeroable { +/// a: T, +/// } +/// ZeroableWhenTIsZeroable::::zeroed(); +/// ``` +/// +/// ```rust,compile_fail +/// # use bytemuck::Zeroable; +/// # #[derive(Clone, Zeroable)] +/// # #[zeroable(bound = "")] +/// # struct ZeroableWhenTIsZeroable { +/// # a: T, +/// # } +/// ZeroableWhenTIsZeroable::::zeroed(); +/// ``` +#[proc_macro_derive(Zeroable, attributes(bytemuck, zeroable))] +pub fn derive_zeroable( + input: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + let expanded = + derive_marker_trait::(parse_macro_input!(input as DeriveInput)); + + proc_macro::TokenStream::from(expanded) +} + +/// Derive the `NoUninit` trait for a struct or enum +/// +/// The macro ensures that the type follows all the the safety requirements +/// for the `NoUninit` trait. +/// +/// The following constraints need to be satisfied for the macro to succeed +/// (the rest of the constraints are guaranteed by the `NoUninit` subtrait +/// bounds, i.e. the type must be `Sized + Copy + 'static`): +/// +/// If applied to a struct: +/// - All fields in the struct must implement `NoUninit` +/// - The struct must be `#[repr(C)]` or `#[repr(transparent)]` +/// - The struct must not contain any padding bytes +/// - The struct must contain no generic parameters +/// +/// If applied to an enum: +/// - The enum must be explicit `#[repr(Int)]`, `#[repr(C)]`, or both +/// - All variants must be fieldless +/// - The enum must contain no generic parameters +#[proc_macro_derive(NoUninit, attributes(bytemuck))] +pub fn derive_no_uninit( + input: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + let expanded = + derive_marker_trait::(parse_macro_input!(input as DeriveInput)); + + proc_macro::TokenStream::from(expanded) +} + +/// Derive the `CheckedBitPattern` trait for a struct or enum. +/// +/// The macro ensures that the type follows all the the safety requirements +/// for the `CheckedBitPattern` trait and derives the required `Bits` type +/// definition and `is_valid_bit_pattern` method for the type automatically. +/// +/// The following constraints need to be satisfied for the macro to succeed: +/// +/// If applied to a struct: +/// - All fields must implement `CheckedBitPattern` +/// - The struct must be `#[repr(C)]` or `#[repr(transparent)]` +/// - The struct must contain no generic parameters +/// +/// If applied to an enum: +/// - The enum must be explicit `#[repr(Int)]` +/// - All fields in variants must implement `CheckedBitPattern` +/// - The enum must contain no generic parameters +#[proc_macro_derive(CheckedBitPattern)] +pub fn derive_maybe_pod( + input: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + let expanded = derive_marker_trait::(parse_macro_input!( + input as DeriveInput + )); + + proc_macro::TokenStream::from(expanded) +} + +/// Derive the `TransparentWrapper` trait for a struct +/// +/// The macro ensures that the struct follows all the the safety requirements +/// for the `TransparentWrapper` trait. +/// +/// The following constraints need to be satisfied for the macro to succeed +/// +/// - The struct must be `#[repr(transparent)]` +/// - The struct must contain the `Wrapped` type +/// - Any ZST fields must be [`Zeroable`][derive@Zeroable]. +/// +/// If the struct only contains a single field, the `Wrapped` type will +/// automatically be determined. If there is more then one field in the struct, +/// you need to specify the `Wrapped` type using `#[transparent(T)]` +/// +/// ## Examples +/// +/// ```rust +/// # use bytemuck_derive::TransparentWrapper; +/// # use std::marker::PhantomData; +/// #[derive(Copy, Clone, TransparentWrapper)] +/// #[repr(transparent)] +/// #[transparent(u16)] +/// struct Test { +/// inner: u16, +/// extra: PhantomData, +/// } +/// ``` +/// +/// If the struct contains more than one field, the `Wrapped` type must be +/// explicitly specified. +/// +/// ```rust,compile_fail +/// # use bytemuck_derive::TransparentWrapper; +/// # use std::marker::PhantomData; +/// #[derive(Copy, Clone, TransparentWrapper)] +/// #[repr(transparent)] +/// // missing `#[transparent(u16)]` +/// struct Test { +/// inner: u16, +/// extra: PhantomData, +/// } +/// ``` +/// +/// Any ZST fields must be `Zeroable`. +/// +/// ```rust,compile_fail +/// # use bytemuck_derive::TransparentWrapper; +/// # use std::marker::PhantomData; +/// struct NonTransparentSafeZST; +/// +/// #[derive(TransparentWrapper)] +/// #[repr(transparent)] +/// #[transparent(u16)] +/// struct Test { +/// inner: u16, +/// extra: PhantomData, +/// another_extra: NonTransparentSafeZST, // not `Zeroable` +/// } +/// ``` +#[proc_macro_derive(TransparentWrapper, attributes(bytemuck, transparent))] +pub fn derive_transparent( + input: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + let expanded = derive_marker_trait::(parse_macro_input!( + input as DeriveInput + )); + + proc_macro::TokenStream::from(expanded) +} + +/// Derive the `Contiguous` trait for an enum +/// +/// The macro ensures that the enum follows all the the safety requirements +/// for the `Contiguous` trait. +/// +/// The following constraints need to be satisfied for the macro to succeed +/// +/// - The enum must be `#[repr(Int)]` +/// - The enum must be fieldless +/// - The enum discriminants must form a contiguous range +/// +/// ## Example +/// +/// ```rust +/// # use bytemuck_derive::{Contiguous}; +/// +/// #[derive(Copy, Clone, Contiguous)] +/// #[repr(u8)] +/// enum Test { +/// A = 0, +/// B = 1, +/// C = 2, +/// } +/// ``` +#[proc_macro_derive(Contiguous)] +pub fn derive_contiguous( + input: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + let expanded = + derive_marker_trait::(parse_macro_input!(input as DeriveInput)); + + proc_macro::TokenStream::from(expanded) +} + +/// Derive the `PartialEq` and `Eq` trait for a type +/// +/// The macro implements `PartialEq` and `Eq` by casting both sides of the +/// comparison to a byte slice and then compares those. +/// +/// ## Warning +/// +/// Since this implements a byte wise comparison, the behavior of floating point +/// numbers does not match their usual comparison behavior. Additionally other +/// custom comparison behaviors of the individual fields are also ignored. This +/// also does not implement `StructuralPartialEq` / `StructuralEq` like +/// `PartialEq` / `Eq` would. This means you can't pattern match on the values. +/// +/// ## Examples +/// +/// ```rust +/// # use bytemuck_derive::{ByteEq, NoUninit}; +/// #[derive(Copy, Clone, NoUninit, ByteEq)] +/// #[repr(C)] +/// struct Test { +/// a: u32, +/// b: char, +/// c: f32, +/// } +/// ``` +/// +/// ```rust +/// # use bytemuck_derive::ByteEq; +/// # use bytemuck::NoUninit; +/// #[derive(Copy, Clone, ByteEq)] +/// #[repr(C)] +/// struct Test { +/// a: [u32; N], +/// } +/// unsafe impl NoUninit for Test {} +/// ``` +#[proc_macro_derive(ByteEq)] +pub fn derive_byte_eq( + input: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let crate_name = bytemuck_crate_name(&input); + let ident = input.ident; + let (impl_generics, ty_generics, where_clause) = + input.generics.split_for_impl(); + + proc_macro::TokenStream::from(quote! { + impl #impl_generics ::core::cmp::PartialEq for #ident #ty_generics #where_clause { + #[inline] + #[must_use] + fn eq(&self, other: &Self) -> bool { + #crate_name::bytes_of(self) == #crate_name::bytes_of(other) + } + } + impl #impl_generics ::core::cmp::Eq for #ident #ty_generics #where_clause { } + }) +} + +/// Derive the `Hash` trait for a type +/// +/// The macro implements `Hash` by casting the value to a byte slice and hashing +/// that. +/// +/// ## Warning +/// +/// The hash does not match the standard library's `Hash` derive. +/// +/// ## Examples +/// +/// ```rust +/// # use bytemuck_derive::{ByteHash, NoUninit}; +/// #[derive(Copy, Clone, NoUninit, ByteHash)] +/// #[repr(C)] +/// struct Test { +/// a: u32, +/// b: char, +/// c: f32, +/// } +/// ``` +/// +/// ```rust +/// # use bytemuck_derive::ByteHash; +/// # use bytemuck::NoUninit; +/// #[derive(Copy, Clone, ByteHash)] +/// #[repr(C)] +/// struct Test { +/// a: [u32; N], +/// } +/// unsafe impl NoUninit for Test {} +/// ``` +#[proc_macro_derive(ByteHash)] +pub fn derive_byte_hash( + input: proc_macro::TokenStream, +) -> proc_macro::TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let crate_name = bytemuck_crate_name(&input); + let ident = input.ident; + let (impl_generics, ty_generics, where_clause) = + input.generics.split_for_impl(); + + proc_macro::TokenStream::from(quote! { + impl #impl_generics ::core::hash::Hash for #ident #ty_generics #where_clause { + #[inline] + fn hash(&self, state: &mut H) { + ::core::hash::Hash::hash_slice(#crate_name::bytes_of(self), state) + } + + #[inline] + fn hash_slice(data: &[Self], state: &mut H) { + ::core::hash::Hash::hash_slice(#crate_name::cast_slice::<_, u8>(data), state) + } + } + }) +} + +/// Basic wrapper for error handling +fn derive_marker_trait(input: DeriveInput) -> TokenStream { + derive_marker_trait_inner::(input) + .unwrap_or_else(|err| err.into_compile_error()) +} + +/// Find `#[name(key = "value")]` helper attributes on the struct, and return +/// their `"value"`s parsed with `parser`. +/// +/// Returns an error if any attributes with the given `name` do not match the +/// expected format. Returns `Ok([])` if no attributes with `name` are found. +fn find_and_parse_helper_attributes( + attributes: &[syn::Attribute], name: &str, key: &str, parser: P, + example_value: &str, invalid_value_msg: &str, +) -> Result> { + let invalid_format_msg = + format!("{name} attribute must be `{name}({key} = \"{example_value}\")`",); + let values_to_check = attributes.iter().filter_map(|attr| match &attr.meta { + // If a `Path` matches our `name`, return an error, else ignore it. + // e.g. `#[zeroable]` + syn::Meta::Path(path) => path + .is_ident(name) + .then(|| Err(syn::Error::new_spanned(path, &invalid_format_msg))), + // If a `NameValue` matches our `name`, return an error, else ignore it. + // e.g. `#[zeroable = "hello"]` + syn::Meta::NameValue(namevalue) => { + namevalue.path.is_ident(name).then(|| { + Err(syn::Error::new_spanned(&namevalue.path, &invalid_format_msg)) + }) + } + // If a `List` matches our `name`, match its contents to our format, else + // ignore it. If its contents match our format, return the value, else + // return an error. + syn::Meta::List(list) => list.path.is_ident(name).then(|| { + let namevalue: syn::MetaNameValue = syn::parse2(list.tokens.clone()) + .map_err(|_| { + syn::Error::new_spanned(&list.tokens, &invalid_format_msg) + })?; + if namevalue.path.is_ident(key) { + match namevalue.value { + syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Str(strlit), .. + }) => Ok(strlit), + _ => { + Err(syn::Error::new_spanned(&namevalue.path, &invalid_format_msg)) + } + } + } else { + Err(syn::Error::new_spanned(&namevalue.path, &invalid_format_msg)) + } + }), + }); + // Parse each value found with the given parser, and return them if no errors + // occur. + values_to_check + .map(|lit| { + let lit = lit?; + lit.parse_with(parser).map_err(|err| { + syn::Error::new_spanned(&lit, format!("{invalid_value_msg}: {err}")) + }) + }) + .collect() +} + +fn derive_marker_trait_inner( + mut input: DeriveInput, +) -> Result { + let crate_name = bytemuck_crate_name(&input); + let trait_ = Trait::ident(&input, &crate_name)?; + // If this trait allows explicit bounds, and any explicit bounds were given, + // then use those explicit bounds. Else, apply the default bounds (bound + // each generic type on this trait). + if let Some(name) = Trait::explicit_bounds_attribute_name() { + // See if any explicit bounds were given in attributes. + let explicit_bounds = find_and_parse_helper_attributes( + &input.attrs, + name, + "bound", + >::parse_terminated, + "Type: Trait", + "invalid where predicate", + )?; + + if !explicit_bounds.is_empty() { + // Explicit bounds were given. + // Enforce explicitly given bounds, and emit "perfect derive" (i.e. add + // bounds for each field's type). + let explicit_bounds = explicit_bounds + .into_iter() + .flatten() + .collect::>(); + + let fields = match (Trait::perfect_derive_fields(&input), &input.data) { + (Some(fields), _) => fields, + (None, syn::Data::Struct(syn::DataStruct { fields, .. })) => { + fields.clone() + } + (None, syn::Data::Union(_)) => { + return Err(syn::Error::new_spanned( + trait_, + &"perfect derive is not supported for unions", + )); + } + (None, syn::Data::Enum(_)) => { + return Err(syn::Error::new_spanned( + trait_, + &"perfect derive is not supported for enums", + )); + } + }; + + let predicates = &mut input.generics.make_where_clause().predicates; + + predicates.extend(explicit_bounds); + + for field in fields { + let ty = field.ty; + predicates.push(syn::parse_quote!( + #ty: #trait_ + )); + } + } else { + // No explicit bounds were given. + // Enforce trait bound on all type generics. + add_trait_marker(&mut input.generics, &trait_); + } + } else { + // This trait does not allow explicit bounds. + // Enforce trait bound on all type generics. + add_trait_marker(&mut input.generics, &trait_); + } + + let name = &input.ident; + + let (impl_generics, ty_generics, where_clause) = + input.generics.split_for_impl(); + + Trait::check_attributes(&input.data, &input.attrs)?; + let asserts = Trait::asserts(&input, &crate_name)?; + let (trait_impl_extras, trait_impl) = Trait::trait_impl(&input, &crate_name)?; + + let implies_trait = if let Some(implies_trait) = + Trait::implies_trait(&crate_name) + { + quote!(unsafe impl #impl_generics #implies_trait for #name #ty_generics #where_clause {}) + } else { + quote!() + }; + + let where_clause = + if Trait::requires_where_clause() { where_clause } else { None }; + + Ok(quote! { + #asserts + + #trait_impl_extras + + unsafe impl #impl_generics #trait_ for #name #ty_generics #where_clause { + #trait_impl + } + + #implies_trait + }) +} + +/// Add a trait marker to the generics if it is not already present +fn add_trait_marker(generics: &mut syn::Generics, trait_name: &syn::Path) { + // Get each generic type parameter. + let type_params = generics + .type_params() + .map(|param| ¶m.ident) + .map(|param| { + syn::parse_quote!( + #param: #trait_name + ) + }) + .collect::>(); + + generics.make_where_clause().predicates.extend(type_params); +} diff --git a/third_party/rust/bytemuck_derive/src/traits.rs b/third_party/rust/bytemuck_derive/src/traits.rs new file mode 100644 index 000000000000..f8cc01c46cde --- /dev/null +++ b/third_party/rust/bytemuck_derive/src/traits.rs @@ -0,0 +1,1460 @@ +#![allow(unused_imports)] +use std::{cmp, convert::TryFrom}; + +use proc_macro2::{Ident, Span, TokenStream, TokenTree}; +use quote::{quote, ToTokens}; +use syn::{ + parse::{Parse, ParseStream, Parser}, + punctuated::Punctuated, + spanned::Spanned, + Result, *, +}; + +macro_rules! bail { + ($msg:expr $(,)?) => { + return Err(Error::new(Span::call_site(), &$msg[..])) + }; + + ( $msg:expr => $span_to_blame:expr $(,)? ) => { + return Err(Error::new_spanned(&$span_to_blame, $msg)) + }; +} + +pub trait Derivable { + fn ident(input: &DeriveInput, crate_name: &TokenStream) -> Result; + fn implies_trait(_crate_name: &TokenStream) -> Option { + None + } + fn asserts( + _input: &DeriveInput, _crate_name: &TokenStream, + ) -> Result { + Ok(quote!()) + } + fn check_attributes(_ty: &Data, _attributes: &[Attribute]) -> Result<()> { + Ok(()) + } + fn trait_impl( + _input: &DeriveInput, _crate_name: &TokenStream, + ) -> Result<(TokenStream, TokenStream)> { + Ok((quote!(), quote!())) + } + fn requires_where_clause() -> bool { + true + } + fn explicit_bounds_attribute_name() -> Option<&'static str> { + None + } + + /// If this trait has a custom meaning for "perfect derive", this function + /// should be overridden to return `Some`. + /// + /// The default is "the fields of a struct; unions and enums not supported". + fn perfect_derive_fields(_input: &DeriveInput) -> Option { + None + } +} + +pub struct Pod; + +impl Derivable for Pod { + fn ident(_: &DeriveInput, crate_name: &TokenStream) -> Result { + Ok(syn::parse_quote!(#crate_name::Pod)) + } + + fn asserts( + input: &DeriveInput, crate_name: &TokenStream, + ) -> Result { + let repr = get_repr(&input.attrs)?; + + let completly_packed = + repr.packed == Some(1) || repr.repr == Repr::Transparent; + + if !completly_packed && !input.generics.params.is_empty() { + bail!("\ + Pod requires cannot be derived for non-packed types containing \ + generic parameters because the padding requirements can't be verified \ + for generic non-packed structs\ + " => input.generics.params.first().unwrap()); + } + + match &input.data { + Data::Struct(_) => { + let assert_no_padding = if !completly_packed { + Some(generate_assert_no_padding(input)?) + } else { + None + }; + let assert_fields_are_pod = generate_fields_are_trait( + input, + None, + Self::ident(input, crate_name)?, + )?; + + Ok(quote!( + #assert_no_padding + #assert_fields_are_pod + )) + } + Data::Enum(_) => bail!("Deriving Pod is not supported for enums"), + Data::Union(_) => bail!("Deriving Pod is not supported for unions"), + } + } + + fn check_attributes(_ty: &Data, attributes: &[Attribute]) -> Result<()> { + let repr = get_repr(attributes)?; + match repr.repr { + Repr::C => Ok(()), + Repr::Transparent => Ok(()), + _ => { + bail!("Pod requires the type to be #[repr(C)] or #[repr(transparent)]") + } + } + } +} + +pub struct AnyBitPattern; + +impl Derivable for AnyBitPattern { + fn ident(_: &DeriveInput, crate_name: &TokenStream) -> Result { + Ok(syn::parse_quote!(#crate_name::AnyBitPattern)) + } + + fn implies_trait(crate_name: &TokenStream) -> Option { + Some(quote!(#crate_name::Zeroable)) + } + + fn asserts( + input: &DeriveInput, crate_name: &TokenStream, + ) -> Result { + match &input.data { + Data::Union(_) => Ok(quote!()), // unions are always `AnyBitPattern` + Data::Struct(_) => { + generate_fields_are_trait(input, None, Self::ident(input, crate_name)?) + } + Data::Enum(_) => { + bail!("Deriving AnyBitPattern is not supported for enums") + } + } + } +} + +pub struct Zeroable; + +/// Helper function to get the variant with discriminant zero (implicit or +/// explicit). +fn get_zero_variant(enum_: &DataEnum) -> Result> { + let iter = VariantDiscriminantIterator::new(enum_.variants.iter()); + let mut zero_variant = None; + for res in iter { + let (discriminant, variant) = res?; + if discriminant == 0 { + zero_variant = Some(variant); + break; + } + } + Ok(zero_variant) +} + +impl Derivable for Zeroable { + fn ident(_: &DeriveInput, crate_name: &TokenStream) -> Result { + Ok(syn::parse_quote!(#crate_name::Zeroable)) + } + + fn check_attributes(ty: &Data, attributes: &[Attribute]) -> Result<()> { + let repr = get_repr(attributes)?; + match ty { + Data::Struct(_) => Ok(()), + Data::Enum(_) => { + if !matches!( + repr.repr, + Repr::C | Repr::Integer(_) | Repr::CWithDiscriminant(_) + ) { + bail!("Zeroable requires the enum to be an explicit #[repr(Int)] and/or #[repr(C)]") + } + + // We ensure there is a zero variant in `asserts`, since it is needed + // there anyway. + + Ok(()) + } + Data::Union(_) => Ok(()), + } + } + + fn asserts( + input: &DeriveInput, crate_name: &TokenStream, + ) -> Result { + match &input.data { + Data::Union(_) => Ok(quote!()), // unions are always `Zeroable` + Data::Struct(_) => { + generate_fields_are_trait(input, None, Self::ident(input, crate_name)?) + } + Data::Enum(enum_) => { + let zero_variant = get_zero_variant(enum_)?; + + if zero_variant.is_none() { + bail!("No variant's discriminant is 0") + }; + + generate_fields_are_trait( + input, + zero_variant, + Self::ident(input, crate_name)?, + ) + } + } + } + + fn explicit_bounds_attribute_name() -> Option<&'static str> { + Some("zeroable") + } + + fn perfect_derive_fields(input: &DeriveInput) -> Option { + match &input.data { + Data::Struct(struct_) => Some(struct_.fields.clone()), + Data::Enum(enum_) => { + // We handle `Err` returns from `get_zero_variant` in `asserts`, so it's + // fine to just ignore them here and return `None`. + // Otherwise, we clone the `fields` of the zero variant (if any). + Some(get_zero_variant(enum_).ok()??.fields.clone()) + } + Data::Union(_) => None, + } + } +} + +pub struct NoUninit; + +impl Derivable for NoUninit { + fn ident(_: &DeriveInput, crate_name: &TokenStream) -> Result { + Ok(syn::parse_quote!(#crate_name::NoUninit)) + } + + fn check_attributes(ty: &Data, attributes: &[Attribute]) -> Result<()> { + let repr = get_repr(attributes)?; + match ty { + Data::Struct(_) => match repr.repr { + Repr::C | Repr::Transparent => Ok(()), + _ => bail!("NoUninit requires the struct to be #[repr(C)] or #[repr(transparent)]"), + }, + Data::Enum(_) => if repr.repr.is_integer() { + Ok(()) + } else { + bail!("NoUninit requires the enum to be an explicit #[repr(Int)]") + }, + Data::Union(_) => bail!("NoUninit can only be derived on enums and structs") + } + } + + fn asserts( + input: &DeriveInput, crate_name: &TokenStream, + ) -> Result { + if !input.generics.params.is_empty() { + bail!("NoUninit cannot be derived for structs containing generic parameters because the padding requirements can't be verified for generic structs"); + } + + match &input.data { + Data::Struct(DataStruct { .. }) => { + let assert_no_padding = generate_assert_no_padding(&input)?; + let assert_fields_are_no_padding = generate_fields_are_trait( + &input, + None, + Self::ident(input, crate_name)?, + )?; + + Ok(quote!( + #assert_no_padding + #assert_fields_are_no_padding + )) + } + Data::Enum(DataEnum { variants, .. }) => { + if variants.iter().any(|variant| !variant.fields.is_empty()) { + bail!("Only fieldless enums are supported for NoUninit") + } else { + Ok(quote!()) + } + } + Data::Union(_) => bail!("NoUninit cannot be derived for unions"), /* shouldn't be possible since we already error in attribute check for this case */ + } + } + + fn trait_impl( + _input: &DeriveInput, _crate_name: &TokenStream, + ) -> Result<(TokenStream, TokenStream)> { + Ok((quote!(), quote!())) + } +} + +pub struct CheckedBitPattern; + +impl Derivable for CheckedBitPattern { + fn ident(_: &DeriveInput, crate_name: &TokenStream) -> Result { + Ok(syn::parse_quote!(#crate_name::CheckedBitPattern)) + } + + fn check_attributes(ty: &Data, attributes: &[Attribute]) -> Result<()> { + let repr = get_repr(attributes)?; + match ty { + Data::Struct(_) => match repr.repr { + Repr::C | Repr::Transparent => Ok(()), + _ => bail!("CheckedBitPattern derive requires the struct to be #[repr(C)] or #[repr(transparent)]"), + }, + Data::Enum(DataEnum { variants,.. }) => { + if !enum_has_fields(variants.iter()){ + if repr.repr.is_integer() { + Ok(()) + } else { + bail!("CheckedBitPattern requires the enum to be an explicit #[repr(Int)]") + } + } else if matches!(repr.repr, Repr::Rust) { + bail!("CheckedBitPattern requires an explicit repr annotation because `repr(Rust)` doesn't have a specified type layout") + } else { + Ok(()) + } + } + Data::Union(_) => bail!("CheckedBitPattern can only be derived on enums and structs") + } + } + + fn asserts( + input: &DeriveInput, crate_name: &TokenStream, + ) -> Result { + if !input.generics.params.is_empty() { + bail!("CheckedBitPattern cannot be derived for structs containing generic parameters"); + } + + match &input.data { + Data::Struct(DataStruct { .. }) => { + let assert_fields_are_maybe_pod = generate_fields_are_trait( + &input, + None, + Self::ident(input, crate_name)?, + )?; + + Ok(assert_fields_are_maybe_pod) + } + // nothing needed, already guaranteed OK by NoUninit. + Data::Enum(_) => Ok(quote!()), + Data::Union(_) => bail!("Internal error in CheckedBitPattern derive"), /* shouldn't be possible since we already error in attribute check for this case */ + } + } + + fn trait_impl( + input: &DeriveInput, crate_name: &TokenStream, + ) -> Result<(TokenStream, TokenStream)> { + match &input.data { + Data::Struct(DataStruct { fields, .. }) => { + generate_checked_bit_pattern_struct( + &input.ident, + fields, + &input.attrs, + crate_name, + ) + } + Data::Enum(DataEnum { variants, .. }) => { + generate_checked_bit_pattern_enum(input, variants, crate_name) + } + Data::Union(_) => bail!("Internal error in CheckedBitPattern derive"), /* shouldn't be possible since we already error in attribute check for this case */ + } + } +} + +pub struct TransparentWrapper; + +impl TransparentWrapper { + fn get_wrapper_type( + attributes: &[Attribute], fields: &Fields, + ) -> Option { + let transparent_param = get_simple_attr(attributes, "transparent"); + transparent_param.map(|ident| ident.to_token_stream()).or_else(|| { + let mut types = get_field_types(&fields); + let first_type = types.next(); + if let Some(_) = types.next() { + // can't guess param type if there is more than one field + return None; + } else { + first_type.map(|ty| ty.to_token_stream()) + } + }) + } +} + +impl Derivable for TransparentWrapper { + fn ident(input: &DeriveInput, crate_name: &TokenStream) -> Result { + let fields = get_struct_fields(input)?; + + let ty = match Self::get_wrapper_type(&input.attrs, &fields) { + Some(ty) => ty, + None => bail!( + "\ + when deriving TransparentWrapper for a struct with more than one field \ + you need to specify the transparent field using #[transparent(T)]\ + " + ), + }; + + Ok(syn::parse_quote!(#crate_name::TransparentWrapper<#ty>)) + } + + fn asserts( + input: &DeriveInput, crate_name: &TokenStream, + ) -> Result { + let (impl_generics, _ty_generics, where_clause) = + input.generics.split_for_impl(); + let fields = get_struct_fields(input)?; + let wrapped_type = match Self::get_wrapper_type(&input.attrs, &fields) { + Some(wrapped_type) => wrapped_type.to_string(), + None => unreachable!(), /* other code will already reject this derive */ + }; + let mut wrapped_field_ty = None; + let mut nonwrapped_field_tys = vec![]; + for field in fields.iter() { + let field_ty = &field.ty; + if field_ty.to_token_stream().to_string() == wrapped_type { + if wrapped_field_ty.is_some() { + bail!( + "TransparentWrapper can only have one field of the wrapped type" + ); + } + wrapped_field_ty = Some(field_ty); + } else { + nonwrapped_field_tys.push(field_ty); + } + } + if let Some(wrapped_field_ty) = wrapped_field_ty { + Ok(quote!( + const _: () = { + #[repr(transparent)] + #[allow(clippy::multiple_bound_locations)] + struct AssertWrappedIsWrapped #impl_generics((u8, ::core::marker::PhantomData<#wrapped_field_ty>), #(#nonwrapped_field_tys),*) #where_clause; + fn assert_zeroable() {} + #[allow(clippy::multiple_bound_locations)] + fn check #impl_generics () #where_clause { + #( + assert_zeroable::<#nonwrapped_field_tys>(); + )* + } + }; + )) + } else { + bail!("TransparentWrapper must have one field of the wrapped type") + } + } + + fn check_attributes(_ty: &Data, attributes: &[Attribute]) -> Result<()> { + let repr = get_repr(attributes)?; + + match repr.repr { + Repr::Transparent => Ok(()), + _ => { + bail!( + "TransparentWrapper requires the struct to be #[repr(transparent)]" + ) + } + } + } + + fn requires_where_clause() -> bool { + false + } +} + +pub struct Contiguous; + +impl Derivable for Contiguous { + fn ident(_: &DeriveInput, crate_name: &TokenStream) -> Result { + Ok(syn::parse_quote!(#crate_name::Contiguous)) + } + + fn trait_impl( + input: &DeriveInput, _crate_name: &TokenStream, + ) -> Result<(TokenStream, TokenStream)> { + let repr = get_repr(&input.attrs)?; + + let integer_ty = if let Some(integer_ty) = repr.repr.as_integer() { + integer_ty + } else { + bail!("Contiguous requires the enum to be #[repr(Int)]"); + }; + + let variants = get_enum_variants(input)?; + if enum_has_fields(variants.clone()) { + return Err(Error::new_spanned( + &input, + "Only fieldless enums are supported", + )); + } + + let mut variants_with_discriminant = + VariantDiscriminantIterator::new(variants); + + let (min, max, count) = variants_with_discriminant.try_fold( + (i128::MAX, i128::MIN, 0), + |(min, max, count), res| { + let (discriminant, _variant) = res?; + Ok::<_, Error>(( + i128::min(min, discriminant), + i128::max(max, discriminant), + count + 1, + )) + }, + )?; + + if max - min != count - 1 { + bail! { + "Contiguous requires the enum discriminants to be contiguous", + } + } + + let min_lit = LitInt::new(&format!("{}", min), input.span()); + let max_lit = LitInt::new(&format!("{}", max), input.span()); + + // `from_integer` and `into_integer` are usually provided by the trait's + // default implementation. We override this implementation because it + // goes through `transmute_copy`, which can lead to inefficient assembly as seen in https://github.com/Lokathor/bytemuck/issues/175 . + + Ok(( + quote!(), + quote! { + type Int = #integer_ty; + + #[allow(clippy::missing_docs_in_private_items)] + const MIN_VALUE: #integer_ty = #min_lit; + + #[allow(clippy::missing_docs_in_private_items)] + const MAX_VALUE: #integer_ty = #max_lit; + + #[inline] + fn from_integer(value: Self::Int) -> Option { + #[allow(clippy::manual_range_contains)] + if Self::MIN_VALUE <= value && value <= Self::MAX_VALUE { + Some(unsafe { ::core::mem::transmute(value) }) + } else { + None + } + } + + #[inline] + fn into_integer(self) -> Self::Int { + self as #integer_ty + } + }, + )) + } +} + +fn get_struct_fields(input: &DeriveInput) -> Result<&Fields> { + if let Data::Struct(DataStruct { fields, .. }) = &input.data { + Ok(fields) + } else { + bail!("deriving this trait is only supported for structs") + } +} + +/// Extract the `Fields` off a `DeriveInput`, or, in the `enum` case, off +/// those of the `enum_variant`, when provided (e.g., for `Zeroable`). +/// +/// We purposely allow not providing an `enum_variant` for cases where +/// the caller wants to reject supporting `enum`s (e.g., `NoPadding`). +fn get_fields( + input: &DeriveInput, enum_variant: Option<&Variant>, +) -> Result { + match &input.data { + Data::Struct(DataStruct { fields, .. }) => Ok(fields.clone()), + Data::Union(DataUnion { fields, .. }) => Ok(Fields::Named(fields.clone())), + Data::Enum(_) => match enum_variant { + Some(variant) => Ok(variant.fields.clone()), + None => bail!("deriving this trait is not supported for enums"), + }, + } +} + +fn get_enum_variants<'a>( + input: &'a DeriveInput, +) -> Result + Clone + 'a> { + if let Data::Enum(DataEnum { variants, .. }) = &input.data { + Ok(variants.iter()) + } else { + bail!("deriving this trait is only supported for enums") + } +} + +fn get_field_types<'a>( + fields: &'a Fields, +) -> impl Iterator + 'a { + fields.iter().map(|field| &field.ty) +} + +fn generate_checked_bit_pattern_struct( + input_ident: &Ident, fields: &Fields, attrs: &[Attribute], + crate_name: &TokenStream, +) -> Result<(TokenStream, TokenStream)> { + let bits_ty = Ident::new(&format!("{}Bits", input_ident), input_ident.span()); + + let repr = get_repr(attrs)?; + + let field_names = fields + .iter() + .enumerate() + .map(|(i, field)| { + field.ident.clone().unwrap_or_else(|| { + Ident::new(&format!("field{}", i), input_ident.span()) + }) + }) + .collect::>(); + let field_tys = fields.iter().map(|field| &field.ty).collect::>(); + + let field_name = &field_names[..]; + let field_ty = &field_tys[..]; + + Ok(( + quote! { + #[doc = #GENERATED_TYPE_DOCUMENTATION] + #repr + #[derive(Clone, Copy, #crate_name::AnyBitPattern)] + #[allow(missing_docs)] + pub struct #bits_ty { + #(#field_name: <#field_ty as #crate_name::CheckedBitPattern>::Bits,)* + } + + #[allow(unexpected_cfgs)] + const _: () = { + #[cfg(not(target_arch = "spirv"))] + impl ::core::fmt::Debug for #bits_ty { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut debug_struct = ::core::fmt::Formatter::debug_struct(f, ::core::stringify!(#bits_ty)); + #(::core::fmt::DebugStruct::field(&mut debug_struct, ::core::stringify!(#field_name), &{ self.#field_name });)* + ::core::fmt::DebugStruct::finish(&mut debug_struct) + } + } + }; + }, + quote! { + type Bits = #bits_ty; + + #[inline] + #[allow(clippy::double_comparisons, unused)] + fn is_valid_bit_pattern(bits: &#bits_ty) -> bool { + #(<#field_ty as #crate_name::CheckedBitPattern>::is_valid_bit_pattern(&{ bits.#field_name }) && )* true + } + }, + )) +} + +fn generate_checked_bit_pattern_enum( + input: &DeriveInput, variants: &Punctuated, + crate_name: &TokenStream, +) -> Result<(TokenStream, TokenStream)> { + if enum_has_fields(variants.iter()) { + generate_checked_bit_pattern_enum_with_fields(input, variants, crate_name) + } else { + generate_checked_bit_pattern_enum_without_fields(input, variants) + } +} + +fn generate_checked_bit_pattern_enum_without_fields( + input: &DeriveInput, variants: &Punctuated, +) -> Result<(TokenStream, TokenStream)> { + let span = input.span(); + let mut variants_with_discriminant = + VariantDiscriminantIterator::new(variants.iter()); + + let (min, max, count) = variants_with_discriminant.try_fold( + (i128::MAX, i128::MIN, 0), + |(min, max, count), res| { + let (discriminant, _variant) = res?; + Ok::<_, Error>(( + i128::min(min, discriminant), + i128::max(max, discriminant), + count + 1, + )) + }, + )?; + + let check = if count == 0 { + quote!(false) + } else if max - min == count - 1 { + // contiguous range + let min_lit = LitInt::new(&format!("{}", min), span); + let max_lit = LitInt::new(&format!("{}", max), span); + + quote!(*bits >= #min_lit && *bits <= #max_lit) + } else { + // not contiguous range, check for each + let variant_discriminant_lits = + VariantDiscriminantIterator::new(variants.iter()) + .map(|res| { + let (discriminant, _variant) = res?; + Ok(LitInt::new(&format!("{}", discriminant), span)) + }) + .collect::>>()?; + + // count is at least 1 + let first = &variant_discriminant_lits[0]; + let rest = &variant_discriminant_lits[1..]; + + quote!(matches!(*bits, #first #(| #rest )*)) + }; + + let repr = get_repr(&input.attrs)?; + let integer = repr.repr.as_integer().unwrap(); // should be checked in attr check already + Ok(( + quote!(), + quote! { + type Bits = #integer; + + #[inline] + #[allow(clippy::double_comparisons)] + fn is_valid_bit_pattern(bits: &Self::Bits) -> bool { + #check + } + }, + )) +} + +fn generate_checked_bit_pattern_enum_with_fields( + input: &DeriveInput, variants: &Punctuated, + crate_name: &TokenStream, +) -> Result<(TokenStream, TokenStream)> { + let representation = get_repr(&input.attrs)?; + let vis = &input.vis; + + match representation.repr { + Repr::Rust => unreachable!(), + repr @ (Repr::C | Repr::CWithDiscriminant(_)) => { + let integer = match repr { + Repr::C => quote!(::core::ffi::c_int), + Repr::CWithDiscriminant(integer) => quote!(#integer), + _ => unreachable!(), + }; + let input_ident = &input.ident; + + let bits_repr = Representation { repr: Repr::C, ..representation }; + + // the enum manually re-configured as the actual tagged union it + // represents, thus circumventing the requirements rust imposes on + // the tag even when using #[repr(C)] enum layout + // see: https://doc.rust-lang.org/reference/type-layout.html#reprc-enums-with-fields + let bits_ty_ident = + Ident::new(&format!("{input_ident}Bits"), input.span()); + + // the variants union part of the tagged union. These get put into a union + // which gets the AnyBitPattern derive applied to it, thus checking + // that the fields of the union obey the requriements of AnyBitPattern. + // The types that actually go in the union are one more level of + // indirection deep: we generate new structs for each variant + // (`variant_struct_definitions`) which themselves have the + // `CheckedBitPattern` derive applied, thus generating + // `{variant_struct_ident}Bits` structs, which are the ones that go + // into this union. + let variants_union_ident = + Ident::new(&format!("{}Variants", input.ident), input.span()); + + let variant_struct_idents = variants.iter().map(|v| { + Ident::new(&format!("{input_ident}Variant{}", v.ident), v.span()) + }); + + let variant_struct_definitions = + variant_struct_idents.clone().zip(variants.iter()).map(|(variant_struct_ident, v)| { + let fields = v.fields.iter().map(|v| &v.ty); + + quote! { + #[derive(::core::clone::Clone, ::core::marker::Copy, #crate_name::CheckedBitPattern)] + #[repr(C)] + #vis struct #variant_struct_ident(#(#fields),*); + } + }); + + let union_fields = variant_struct_idents + .clone() + .zip(variants.iter()) + .map(|(variant_struct_ident, v)| { + let variant_struct_bits_ident = + Ident::new(&format!("{variant_struct_ident}Bits"), input.span()); + let field_ident = &v.ident; + quote! { + #field_ident: #variant_struct_bits_ident + } + }); + + let variant_checks = variant_struct_idents + .clone() + .zip(VariantDiscriminantIterator::new(variants.iter())) + .zip(variants.iter()) + .map(|((variant_struct_ident, discriminant), v)| -> Result<_> { + let (discriminant, _variant) = discriminant?; + let discriminant = LitInt::new(&discriminant.to_string(), v.span()); + let ident = &v.ident; + Ok(quote! { + #discriminant => { + let payload = unsafe { &bits.payload.#ident }; + <#variant_struct_ident as #crate_name::CheckedBitPattern>::is_valid_bit_pattern(payload) + } + }) + }) + .collect::>>()?; + + Ok(( + quote! { + #[doc = #GENERATED_TYPE_DOCUMENTATION] + #[derive(::core::clone::Clone, ::core::marker::Copy, #crate_name::AnyBitPattern)] + #bits_repr + #vis struct #bits_ty_ident { + tag: #integer, + payload: #variants_union_ident, + } + + #[allow(unexpected_cfgs)] + const _: () = { + #[cfg(not(target_arch = "spirv"))] + impl ::core::fmt::Debug for #bits_ty_ident { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut debug_struct = ::core::fmt::Formatter::debug_struct(f, ::core::stringify!(#bits_ty_ident)); + ::core::fmt::DebugStruct::field(&mut debug_struct, "tag", &self.tag); + ::core::fmt::DebugStruct::field(&mut debug_struct, "payload", &self.payload); + ::core::fmt::DebugStruct::finish(&mut debug_struct) + } + } + }; + + #[derive(::core::clone::Clone, ::core::marker::Copy, #crate_name::AnyBitPattern)] + #[repr(C)] + #[allow(non_snake_case)] + #vis union #variants_union_ident { + #(#union_fields,)* + } + + #[allow(unexpected_cfgs)] + const _: () = { + #[cfg(not(target_arch = "spirv"))] + impl ::core::fmt::Debug for #variants_union_ident { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut debug_struct = ::core::fmt::Formatter::debug_struct(f, ::core::stringify!(#variants_union_ident)); + ::core::fmt::DebugStruct::finish_non_exhaustive(&mut debug_struct) + } + } + }; + + #(#variant_struct_definitions)* + }, + quote! { + type Bits = #bits_ty_ident; + + #[inline] + #[allow(clippy::double_comparisons)] + fn is_valid_bit_pattern(bits: &Self::Bits) -> bool { + match bits.tag { + #(#variant_checks)* + _ => false, + } + } + }, + )) + } + Repr::Transparent => { + if variants.len() != 1 { + bail!("enums with more than one variant cannot be transparent") + } + + let variant = &variants[0]; + + let bits_ty = Ident::new(&format!("{}Bits", input.ident), input.span()); + let fields = variant.fields.iter().map(|v| &v.ty); + + Ok(( + quote! { + #[doc = #GENERATED_TYPE_DOCUMENTATION] + #[derive(::core::clone::Clone, ::core::marker::Copy, #crate_name::CheckedBitPattern)] + #[repr(C)] + #vis struct #bits_ty(#(#fields),*); + }, + quote! { + type Bits = <#bits_ty as #crate_name::CheckedBitPattern>::Bits; + + #[inline] + #[allow(clippy::double_comparisons)] + fn is_valid_bit_pattern(bits: &Self::Bits) -> bool { + <#bits_ty as #crate_name::CheckedBitPattern>::is_valid_bit_pattern(bits) + } + }, + )) + } + Repr::Integer(integer) => { + let bits_repr = Representation { repr: Repr::C, ..representation }; + let input_ident = &input.ident; + + // the enum manually re-configured as the union it represents. such a + // union is the union of variants as a repr(c) struct with the + // discriminator type inserted at the beginning. in our case we + // union the `Bits` representation of each variant rather than the variant + // itself, which we generate via a nested `CheckedBitPattern` derive + // on the `variant_struct_definitions` generated below. + // + // see: https://doc.rust-lang.org/reference/type-layout.html#primitive-representation-of-enums-with-fields + let bits_ty_ident = + Ident::new(&format!("{input_ident}Bits"), input.span()); + + let variant_struct_idents = variants.iter().map(|v| { + Ident::new(&format!("{input_ident}Variant{}", v.ident), v.span()) + }); + + let variant_struct_definitions = + variant_struct_idents.clone().zip(variants.iter()).map(|(variant_struct_ident, v)| { + let fields = v.fields.iter().map(|v| &v.ty); + + // adding the discriminant repr integer as first field, as described above + quote! { + #[derive(::core::clone::Clone, ::core::marker::Copy, #crate_name::CheckedBitPattern)] + #[repr(C)] + #vis struct #variant_struct_ident(#integer, #(#fields),*); + } + }); + + let union_fields = variant_struct_idents + .clone() + .zip(variants.iter()) + .map(|(variant_struct_ident, v)| { + let variant_struct_bits_ident = + Ident::new(&format!("{variant_struct_ident}Bits"), input.span()); + let field_ident = &v.ident; + quote! { + #field_ident: #variant_struct_bits_ident + } + }); + + let variant_checks = variant_struct_idents + .clone() + .zip(VariantDiscriminantIterator::new(variants.iter())) + .zip(variants.iter()) + .map(|((variant_struct_ident, discriminant), v)| -> Result<_> { + let (discriminant, _variant) = discriminant?; + let discriminant = LitInt::new(&discriminant.to_string(), v.span()); + let ident = &v.ident; + Ok(quote! { + #discriminant => { + let payload = unsafe { &bits.#ident }; + <#variant_struct_ident as #crate_name::CheckedBitPattern>::is_valid_bit_pattern(payload) + } + }) + }) + .collect::>>()?; + + Ok(( + quote! { + #[doc = #GENERATED_TYPE_DOCUMENTATION] + #[derive(::core::clone::Clone, ::core::marker::Copy, #crate_name::AnyBitPattern)] + #bits_repr + #[allow(non_snake_case)] + #vis union #bits_ty_ident { + __tag: #integer, + #(#union_fields,)* + } + + #[allow(unexpected_cfgs)] + const _: () = { + #[cfg(not(target_arch = "spirv"))] + impl ::core::fmt::Debug for #bits_ty_ident { + fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { + let mut debug_struct = ::core::fmt::Formatter::debug_struct(f, ::core::stringify!(#bits_ty_ident)); + ::core::fmt::DebugStruct::field(&mut debug_struct, "tag", unsafe { &self.__tag }); + ::core::fmt::DebugStruct::finish_non_exhaustive(&mut debug_struct) + } + } + }; + + #(#variant_struct_definitions)* + }, + quote! { + type Bits = #bits_ty_ident; + + #[inline] + #[allow(clippy::double_comparisons)] + fn is_valid_bit_pattern(bits: &Self::Bits) -> bool { + match unsafe { bits.__tag } { + #(#variant_checks)* + _ => false, + } + } + }, + )) + } + } +} + +/// Check that a struct has no padding by asserting that the size of the struct +/// is equal to the sum of the size of it's fields +fn generate_assert_no_padding(input: &DeriveInput) -> Result { + let struct_type = &input.ident; + let enum_variant = None; // `no padding` check is not supported for `enum`s yet. + let fields = get_fields(input, enum_variant)?; + + let mut field_types = get_field_types(&fields); + let size_sum = if let Some(first) = field_types.next() { + let size_first = quote!(::core::mem::size_of::<#first>()); + let size_rest = quote!(#( + ::core::mem::size_of::<#field_types>() )*); + + quote!(#size_first #size_rest) + } else { + quote!(0) + }; + + Ok(quote! {const _: fn() = || { + #[doc(hidden)] + struct TypeWithoutPadding([u8; #size_sum]); + let _ = ::core::mem::transmute::<#struct_type, TypeWithoutPadding>; + };}) +} + +/// Check that all fields implement a given trait +fn generate_fields_are_trait( + input: &DeriveInput, enum_variant: Option<&Variant>, trait_: syn::Path, +) -> Result { + let (impl_generics, _ty_generics, where_clause) = + input.generics.split_for_impl(); + let fields = get_fields(input, enum_variant)?; + let field_types = get_field_types(&fields); + Ok(quote! {#(const _: fn() = || { + #[allow(clippy::missing_const_for_fn)] + #[doc(hidden)] + fn check #impl_generics () #where_clause { + fn assert_impl() {} + assert_impl::<#field_types>(); + } + };)* + }) +} + +fn get_ident_from_stream(tokens: TokenStream) -> Option { + match tokens.into_iter().next() { + Some(TokenTree::Group(group)) => get_ident_from_stream(group.stream()), + Some(TokenTree::Ident(ident)) => Some(ident), + _ => None, + } +} + +/// get a simple #[foo(bar)] attribute, returning "bar" +fn get_simple_attr(attributes: &[Attribute], attr_name: &str) -> Option { + for attr in attributes { + if let (AttrStyle::Outer, Meta::List(list)) = (&attr.style, &attr.meta) { + if list.path.is_ident(attr_name) { + if let Some(ident) = get_ident_from_stream(list.tokens.clone()) { + return Some(ident); + } + } + } + } + + None +} + +fn get_repr(attributes: &[Attribute]) -> Result { + attributes + .iter() + .filter_map(|attr| { + if attr.path().is_ident("repr") { + Some(attr.parse_args::()) + } else { + None + } + }) + .try_fold(Representation::default(), |a, b| { + let b = b?; + Ok(Representation { + repr: match (a.repr, b.repr) { + (a, Repr::Rust) => a, + (Repr::Rust, b) => b, + _ => bail!("conflicting representation hints"), + }, + packed: match (a.packed, b.packed) { + (a, None) => a, + (None, b) => b, + _ => bail!("conflicting representation hints"), + }, + align: match (a.align, b.align) { + (Some(a), Some(b)) => Some(cmp::max(a, b)), + (a, None) => a, + (None, b) => b, + }, + }) + }) +} + +mk_repr! { + U8 => u8, + I8 => i8, + U16 => u16, + I16 => i16, + U32 => u32, + I32 => i32, + U64 => u64, + I64 => i64, + I128 => i128, + U128 => u128, + Usize => usize, + Isize => isize, +} +// where +macro_rules! mk_repr {( + $( + $Xn:ident => $xn:ident + ),* $(,)? +) => ( + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + enum IntegerRepr { + $($Xn),* + } + + impl<'a> TryFrom<&'a str> for IntegerRepr { + type Error = &'a str; + + fn try_from(value: &'a str) -> std::result::Result { + match value { + $( + stringify!($xn) => Ok(Self::$Xn), + )* + _ => Err(value), + } + } + } + + impl ToTokens for IntegerRepr { + fn to_tokens(&self, tokens: &mut TokenStream) { + match self { + $( + Self::$Xn => tokens.extend(quote!($xn)), + )* + } + } + } +)} +use mk_repr; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Repr { + Rust, + C, + Transparent, + Integer(IntegerRepr), + CWithDiscriminant(IntegerRepr), +} + +impl Repr { + fn is_integer(&self) -> bool { + matches!(self, Self::Integer(..)) + } + + fn as_integer(&self) -> Option { + if let Self::Integer(v) = self { + Some(*v) + } else { + None + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct Representation { + packed: Option, + align: Option, + repr: Repr, +} + +impl Default for Representation { + fn default() -> Self { + Self { packed: None, align: None, repr: Repr::Rust } + } +} + +impl Parse for Representation { + fn parse(input: ParseStream<'_>) -> Result { + let mut ret = Representation::default(); + while !input.is_empty() { + let keyword = input.parse::()?; + // preëmptively call `.to_string()` *once* (rather than on `is_ident()`) + let keyword_str = keyword.to_string(); + let new_repr = match keyword_str.as_str() { + "C" => Repr::C, + "transparent" => Repr::Transparent, + "packed" => { + ret.packed = Some(if input.peek(token::Paren) { + let contents; + parenthesized!(contents in input); + LitInt::base10_parse::(&contents.parse()?)? + } else { + 1 + }); + let _: Option = input.parse()?; + continue; + } + "align" => { + let contents; + parenthesized!(contents in input); + let new_align = LitInt::base10_parse::(&contents.parse()?)?; + ret.align = Some( + ret + .align + .map_or(new_align, |old_align| cmp::max(old_align, new_align)), + ); + let _: Option = input.parse()?; + continue; + } + ident => { + let primitive = IntegerRepr::try_from(ident) + .map_err(|_| input.error("unrecognized representation hint"))?; + Repr::Integer(primitive) + } + }; + ret.repr = match (ret.repr, new_repr) { + (Repr::Rust, new_repr) => { + // This is the first explicit repr. + new_repr + } + (Repr::C, Repr::Integer(integer)) + | (Repr::Integer(integer), Repr::C) => { + // Both the C repr and an integer repr have been specified + // -> merge into a C wit discriminant. + Repr::CWithDiscriminant(integer) + } + (_, _) => { + return Err(input.error("duplicate representation hint")); + } + }; + let _: Option = input.parse()?; + } + Ok(ret) + } +} + +impl ToTokens for Representation { + fn to_tokens(&self, tokens: &mut TokenStream) { + let mut meta = Punctuated::<_, Token![,]>::new(); + + match self.repr { + Repr::Rust => {} + Repr::C => meta.push(quote!(C)), + Repr::Transparent => meta.push(quote!(transparent)), + Repr::Integer(primitive) => meta.push(quote!(#primitive)), + Repr::CWithDiscriminant(primitive) => { + meta.push(quote!(C)); + meta.push(quote!(#primitive)); + } + } + + if let Some(packed) = self.packed.as_ref() { + let lit = LitInt::new(&packed.to_string(), Span::call_site()); + meta.push(quote!(packed(#lit))); + } + + if let Some(align) = self.align.as_ref() { + let lit = LitInt::new(&align.to_string(), Span::call_site()); + meta.push(quote!(align(#lit))); + } + + tokens.extend(quote!( + #[repr(#meta)] + )); + } +} + +fn enum_has_fields<'a>( + mut variants: impl Iterator, +) -> bool { + variants.any(|v| matches!(v.fields, Fields::Named(_) | Fields::Unnamed(_))) +} + +struct VariantDiscriminantIterator<'a, I: Iterator + 'a> { + inner: I, + last_value: i128, +} + +impl<'a, I: Iterator + 'a> + VariantDiscriminantIterator<'a, I> +{ + fn new(inner: I) -> Self { + VariantDiscriminantIterator { inner, last_value: -1 } + } +} + +impl<'a, I: Iterator + 'a> Iterator + for VariantDiscriminantIterator<'a, I> +{ + type Item = Result<(i128, &'a Variant)>; + + fn next(&mut self) -> Option { + let variant = self.inner.next()?; + + if let Some((_, discriminant)) = &variant.discriminant { + let discriminant_value = match parse_int_expr(discriminant) { + Ok(value) => value, + Err(e) => return Some(Err(e)), + }; + self.last_value = discriminant_value; + } else { + // If this wraps, then either: + // 1. the enum is using repr(u128), so wrapping is correct + // 2. the enum is using repr(i<=128 or u<128), so the compiler will + // already emit a "wrapping discriminant" E0370 error. + self.last_value = self.last_value.wrapping_add(1); + // Static assert that there is no integer repr > 128 bits. If that + // changes, the above comment is inaccurate and needs to be updated! + // FIXME(zachs18): maybe should also do something to ensure `isize::BITS + // <= 128`? + if let Some(repr) = None:: { + match repr { + IntegerRepr::U8 + | IntegerRepr::I8 + | IntegerRepr::U16 + | IntegerRepr::I16 + | IntegerRepr::U32 + | IntegerRepr::I32 + | IntegerRepr::U64 + | IntegerRepr::I64 + | IntegerRepr::I128 + | IntegerRepr::U128 + | IntegerRepr::Usize + | IntegerRepr::Isize => (), + } + } + } + + Some(Ok((self.last_value, variant))) + } +} + +fn parse_int_expr(expr: &Expr) -> Result { + match expr { + Expr::Unary(ExprUnary { op: UnOp::Neg(_), expr, .. }) => { + parse_int_expr(expr).map(|int| -int) + } + Expr::Lit(ExprLit { lit: Lit::Int(int), .. }) => int.base10_parse(), + Expr::Lit(ExprLit { lit: Lit::Byte(byte), .. }) => Ok(byte.value().into()), + _ => bail!("Not an integer expression"), + } +} + +#[cfg(test)] +mod tests { + use syn::parse_quote; + + use super::{get_repr, IntegerRepr, Repr, Representation}; + + #[test] + fn parse_basic_repr() { + let attr = parse_quote!(#[repr(C)]); + let repr = get_repr(&[attr]).unwrap(); + assert_eq!(repr, Representation { repr: Repr::C, ..Default::default() }); + + let attr = parse_quote!(#[repr(transparent)]); + let repr = get_repr(&[attr]).unwrap(); + assert_eq!( + repr, + Representation { repr: Repr::Transparent, ..Default::default() } + ); + + let attr = parse_quote!(#[repr(u8)]); + let repr = get_repr(&[attr]).unwrap(); + assert_eq!( + repr, + Representation { + repr: Repr::Integer(IntegerRepr::U8), + ..Default::default() + } + ); + + let attr = parse_quote!(#[repr(packed)]); + let repr = get_repr(&[attr]).unwrap(); + assert_eq!(repr, Representation { packed: Some(1), ..Default::default() }); + + let attr = parse_quote!(#[repr(packed(1))]); + let repr = get_repr(&[attr]).unwrap(); + assert_eq!(repr, Representation { packed: Some(1), ..Default::default() }); + + let attr = parse_quote!(#[repr(packed(2))]); + let repr = get_repr(&[attr]).unwrap(); + assert_eq!(repr, Representation { packed: Some(2), ..Default::default() }); + + let attr = parse_quote!(#[repr(align(2))]); + let repr = get_repr(&[attr]).unwrap(); + assert_eq!(repr, Representation { align: Some(2), ..Default::default() }); + } + + #[test] + fn parse_advanced_repr() { + let attr = parse_quote!(#[repr(align(4), align(2))]); + let repr = get_repr(&[attr]).unwrap(); + assert_eq!(repr, Representation { align: Some(4), ..Default::default() }); + + let attr1 = parse_quote!(#[repr(align(1))]); + let attr2 = parse_quote!(#[repr(align(4))]); + let attr3 = parse_quote!(#[repr(align(2))]); + let repr = get_repr(&[attr1, attr2, attr3]).unwrap(); + assert_eq!(repr, Representation { align: Some(4), ..Default::default() }); + + let attr = parse_quote!(#[repr(C, u8)]); + let repr = get_repr(&[attr]).unwrap(); + assert_eq!( + repr, + Representation { + repr: Repr::CWithDiscriminant(IntegerRepr::U8), + ..Default::default() + } + ); + + let attr = parse_quote!(#[repr(u8, C)]); + let repr = get_repr(&[attr]).unwrap(); + assert_eq!( + repr, + Representation { + repr: Repr::CWithDiscriminant(IntegerRepr::U8), + ..Default::default() + } + ); + } +} + +pub fn bytemuck_crate_name(input: &DeriveInput) -> TokenStream { + const ATTR_NAME: &'static str = "crate"; + + let mut crate_name = quote!(::bytemuck); + for attr in &input.attrs { + if !attr.path().is_ident("bytemuck") { + continue; + } + + attr.parse_nested_meta(|meta| { + if meta.path.is_ident(ATTR_NAME) { + let expr: syn::Expr = meta.value()?.parse()?; + let mut value = &expr; + while let syn::Expr::Group(e) = value { + value = &e.expr; + } + if let syn::Expr::Lit(syn::ExprLit { + lit: syn::Lit::Str(lit), .. + }) = value + { + let suffix = lit.suffix(); + if !suffix.is_empty() { + bail!(format!("Unexpected suffix `{}` on string literal", suffix)) + } + let path: syn::Path = match lit.parse() { + Ok(path) => path, + Err(_) => { + bail!(format!("Failed to parse path: {:?}", lit.value())) + } + }; + crate_name = path.into_token_stream(); + } else { + bail!( + "Expected bytemuck `crate` attribute to be a string: `crate = \"...\"`", + ) + } + } + Ok(()) + }).unwrap(); + } + + return crate_name; +} + +const GENERATED_TYPE_DOCUMENTATION: &str = + " `bytemuck`-generated type for internal purposes only."; diff --git a/third_party/rust/bytemuck_derive/tests/basic.rs b/third_party/rust/bytemuck_derive/tests/basic.rs new file mode 100644 index 000000000000..f76c74978c6a --- /dev/null +++ b/third_party/rust/bytemuck_derive/tests/basic.rs @@ -0,0 +1,524 @@ +#![allow(dead_code)] +#![deny(clippy::allow_attributes)] + +use bytemuck::{ + checked::CheckedCastError, AnyBitPattern, CheckedBitPattern, Contiguous, + NoUninit, Pod, TransparentWrapper, Zeroable, +}; +use std::marker::{PhantomData, PhantomPinned}; + +#[derive(Copy, Clone, Pod, Zeroable)] +#[repr(C)] +struct Test { + a: u16, + b: u16, +} + +#[derive(Pod, Zeroable)] +#[repr(C, packed)] +struct GenericPackedStruct { + a: u32, + b: T, + c: u32, +} + +impl Clone for GenericPackedStruct { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for GenericPackedStruct {} + +#[derive(Pod, Zeroable)] +#[repr(C, packed(1))] +struct GenericPackedStructExplicitPackedAlignment { + a: u32, + b: T, + c: u32, +} + +impl Clone for GenericPackedStructExplicitPackedAlignment { + fn clone(&self) -> Self { + *self + } +} + +impl Copy for GenericPackedStructExplicitPackedAlignment {} + +#[derive(Zeroable)] +struct ZeroGeneric { + a: T, +} + +#[derive(Zeroable)] +#[repr(u8)] +enum ZeroEnum { + A = 0, + B = 1, + C = 2, +} + +#[derive(Zeroable)] +#[repr(u8)] +enum BasicFieldfulZeroEnum { + A(u8) = 0, + B = 1, + C(String) = 2, +} + +#[derive(Zeroable)] +#[repr(C)] +enum ReprCFieldfulZeroEnum { + A(u8), + B(Box<[u8]>), + C, +} + +#[derive(Zeroable)] +#[repr(C, i32)] +enum ReprCIntFieldfulZeroEnum { + B(String) = 1, + A(u8, bool, char) = 0, + C = 2, +} + +#[derive(Zeroable)] +#[repr(i32)] +enum GenericFieldfulZeroEnum { + A(Box) = 1, + B(T, T) = 0, +} + +#[derive(Zeroable)] +#[repr(i32)] +#[zeroable(bound = "")] +enum GenericCustomBoundFieldfulZeroEnum { + A(Option>), + B(String), +} + +#[derive(TransparentWrapper)] +#[repr(transparent)] +struct TransparentSingle { + a: u16, +} + +#[derive(TransparentWrapper)] +#[repr(transparent)] +#[transparent(u16)] +struct TransparentWithZeroSized { + a: u16, + b: PhantomData, +} + +struct MyZst(PhantomData, [u8; 0], PhantomPinned); +unsafe impl Zeroable for MyZst {} + +#[derive(TransparentWrapper)] +#[repr(transparent)] +#[transparent(u16)] +struct TransparentTupleWithCustomZeroSized(u16, MyZst); + +#[repr(u8)] +#[derive(Clone, Copy, Contiguous)] +enum ContiguousWithValues { + A = 0, + B = 1, + C = 2, + D = 3, + E = 4, +} + +#[repr(i8)] +#[derive(Clone, Copy, Contiguous)] +enum ContiguousWithImplicitValues { + A = -10, + B, + C, + D, + E, +} + +#[derive(Copy, Clone, NoUninit)] +#[repr(C)] +struct NoUninitTest { + a: u16, + b: u16, +} + +#[derive(Copy, Clone, AnyBitPattern)] +#[repr(C)] +union UnionTestAnyBitPattern { + a: u8, + b: u16, +} + +#[repr(u8)] +#[derive(Debug, Clone, Copy, NoUninit, CheckedBitPattern, PartialEq, Eq)] +enum CheckedBitPatternEnumWithValues { + A = 0, + B = 1, + C = 2, + D = 3, + E = 4, +} + +#[repr(i8)] +#[derive(Clone, Copy, NoUninit, CheckedBitPattern)] +enum CheckedBitPatternEnumWithImplicitValues { + A = -10, + B, + C, + D, + E, +} + +#[repr(u8)] +#[derive(Debug, Clone, Copy, NoUninit, CheckedBitPattern, PartialEq, Eq)] +enum CheckedBitPatternEnumNonContiguous { + A = 1, + B = 8, + C = 2, + D = 3, + E = 56, +} + +#[repr(u8)] +#[derive(Debug, Clone, Copy, NoUninit, CheckedBitPattern, PartialEq, Eq)] +enum CheckedBitPatternEnumByteLit { + A = b'A', + B = b'B', + C = b'C', + D = b'D', + E = b'E', +} + +#[derive(Debug, Copy, Clone, NoUninit, CheckedBitPattern, PartialEq, Eq)] +#[repr(C)] +struct CheckedBitPatternStruct { + a: u8, + b: CheckedBitPatternEnumNonContiguous, +} + +#[derive(Debug, Copy, Clone, AnyBitPattern, PartialEq, Eq)] +#[repr(C)] +struct AnyBitPatternTest { + a: A, + b: B, +} + +#[derive(Clone, Copy, CheckedBitPattern)] +#[repr(C, align(8))] +struct CheckedBitPatternAlignedStruct { + a: u16, +} + +#[derive(Clone, Copy, CheckedBitPattern)] +#[repr(C, packed)] +struct CheckedBitPatternPackedStruct { + a: u8, + b: u16, +} + +#[derive(Debug, Clone, Copy, CheckedBitPattern, PartialEq, Eq)] +#[repr(C)] +enum CheckedBitPatternCDefaultDiscriminantEnumWithFields { + A(u64), + B { c: u64 }, +} + +#[derive(Debug, Clone, Copy, CheckedBitPattern, PartialEq, Eq)] +#[repr(C, u8)] +enum CheckedBitPatternCEnumWithFields { + A(u32), + B { c: u32 }, +} + +#[derive(Debug, Clone, Copy, CheckedBitPattern, PartialEq, Eq)] +#[repr(u8)] +enum CheckedBitPatternIntEnumWithFields { + A(u8), + B { c: u32 }, +} + +#[derive(Debug, Clone, Copy, CheckedBitPattern, PartialEq, Eq)] +#[repr(transparent)] +enum CheckedBitPatternTransparentEnumWithFields { + A { b: u32 }, +} + +// size 24, align 8. +// first byte always the u8 discriminant, then 7 bytes of padding until the +// payload union since the align of the payload is the greatest of the align of +// all the variants, which is 8 (from +// CheckedBitPatternCDefaultDiscriminantEnumWithFields) +#[derive(Debug, Clone, Copy, CheckedBitPattern, PartialEq, Eq)] +#[repr(C, u8)] +enum CheckedBitPatternEnumNested { + A(CheckedBitPatternCEnumWithFields), + B(CheckedBitPatternCDefaultDiscriminantEnumWithFields), +} + +/// ```compile_fail +/// use bytemuck::{Pod, Zeroable}; +/// +/// #[derive(Pod, Zeroable)] +/// #[repr(transparent)] +/// struct TransparentSingle(T); +/// +/// struct NotPod(u32); +/// +/// let _: u32 = bytemuck::cast(TransparentSingle(NotPod(0u32))); +/// ``` +#[derive( + Debug, Copy, Clone, PartialEq, Eq, Pod, Zeroable, TransparentWrapper, +)] +#[repr(transparent)] +struct NewtypeWrapperTest(T); + +#[test] +fn fails_cast_contiguous() { + let can_cast = CheckedBitPatternEnumWithValues::is_valid_bit_pattern(&5); + assert!(!can_cast); +} + +#[test] +fn passes_cast_contiguous() { + let res = + bytemuck::checked::from_bytes::(&[2u8]); + assert_eq!(*res, CheckedBitPatternEnumWithValues::C); +} + +#[test] +fn fails_cast_noncontiguous() { + let can_cast = CheckedBitPatternEnumNonContiguous::is_valid_bit_pattern(&4); + assert!(!can_cast); +} + +#[test] +fn passes_cast_noncontiguous() { + let res = + bytemuck::checked::from_bytes::(&[ + 56u8, + ]); + assert_eq!(*res, CheckedBitPatternEnumNonContiguous::E); +} + +#[test] +fn fails_cast_bytelit() { + let can_cast = CheckedBitPatternEnumByteLit::is_valid_bit_pattern(&b'a'); + assert!(!can_cast); +} + +#[test] +fn passes_cast_bytelit() { + let res = + bytemuck::checked::cast_slice::(b"CAB"); + assert_eq!( + res, + [ + CheckedBitPatternEnumByteLit::C, + CheckedBitPatternEnumByteLit::A, + CheckedBitPatternEnumByteLit::B + ] + ); +} + +#[test] +fn fails_cast_struct() { + let pod = [0u8, 24u8]; + let res = bytemuck::checked::try_from_bytes::(&pod); + assert!(res.is_err()); +} + +#[test] +fn passes_cast_struct() { + let pod = [0u8, 8u8]; + let res = bytemuck::checked::from_bytes::(&pod); + assert_eq!( + *res, + CheckedBitPatternStruct { a: 0, b: CheckedBitPatternEnumNonContiguous::B } + ); +} + +#[test] +fn anybitpattern_implies_zeroable() { + let test = AnyBitPatternTest::::zeroed(); + assert_eq!(test, AnyBitPatternTest { a: 0isize, b: 0usize }); +} + +#[test] +fn checkedbitpattern_try_pod_read_unaligned() { + let pod = [0u8]; + let res = bytemuck::checked::try_pod_read_unaligned::< + CheckedBitPatternEnumWithValues, + >(&pod); + assert!(res.is_ok()); + + let pod = [5u8]; + let res = bytemuck::checked::try_pod_read_unaligned::< + CheckedBitPatternEnumWithValues, + >(&pod); + assert!(res.is_err()); +} + +#[test] +fn checkedbitpattern_aligned_struct() { + let pod = [0u8; 8]; + bytemuck::checked::pod_read_unaligned::(&pod); +} + +#[test] +fn checkedbitpattern_c_default_discriminant_enum_with_fields() { + let pod = [ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0xcc, + ]; + let value = bytemuck::checked::pod_read_unaligned::< + CheckedBitPatternCDefaultDiscriminantEnumWithFields, + >(&pod); + assert_eq!( + value, + CheckedBitPatternCDefaultDiscriminantEnumWithFields::A(0xcc555555555555cc) + ); + + let pod = [ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xcc, 0x55, 0x55, 0x55, + 0x55, 0x55, 0x55, 0xcc, + ]; + let value = bytemuck::checked::pod_read_unaligned::< + CheckedBitPatternCDefaultDiscriminantEnumWithFields, + >(&pod); + assert_eq!( + value, + CheckedBitPatternCDefaultDiscriminantEnumWithFields::B { + c: 0xcc555555555555cc + } + ); +} + +#[test] +fn checkedbitpattern_c_enum_with_fields() { + let pod = [0x00, 0x00, 0x00, 0x00, 0xcc, 0x55, 0x55, 0xcc]; + let value = bytemuck::checked::pod_read_unaligned::< + CheckedBitPatternCEnumWithFields, + >(&pod); + assert_eq!(value, CheckedBitPatternCEnumWithFields::A(0xcc5555cc)); + + let pod = [0x01, 0x00, 0x00, 0x00, 0xcc, 0x55, 0x55, 0xcc]; + let value = bytemuck::checked::pod_read_unaligned::< + CheckedBitPatternCEnumWithFields, + >(&pod); + assert_eq!(value, CheckedBitPatternCEnumWithFields::B { c: 0xcc5555cc }); +} + +#[test] +fn checkedbitpattern_int_enum_with_fields() { + let pod = [0x00, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + let value = bytemuck::checked::pod_read_unaligned::< + CheckedBitPatternIntEnumWithFields, + >(&pod); + assert_eq!(value, CheckedBitPatternIntEnumWithFields::A(0x55)); + + let pod = [0x01, 0x00, 0x00, 0x00, 0xcc, 0x55, 0x55, 0xcc]; + let value = bytemuck::checked::pod_read_unaligned::< + CheckedBitPatternIntEnumWithFields, + >(&pod); + assert_eq!(value, CheckedBitPatternIntEnumWithFields::B { c: 0xcc5555cc }); +} + +#[test] +fn checkedbitpattern_nested_enum_with_fields() { + // total size 24 bytes. first byte always the u8 discriminant. + + #[repr(C, align(8))] + struct Align8Bytes([u8; 24]); + + // first we'll check variantA, nested variant A + let pod = Align8Bytes([ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, // byte 0 discriminant = 0 = variant A, bytes 1-7 irrelevant padding. + 0x00, 0x00, 0x00, 0x00, 0xcc, 0x55, 0x55, + 0xcc, // bytes 8-15 are the nested CheckedBitPatternCEnumWithFields, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // bytes 16-23 padding + ]); + let value = + bytemuck::checked::from_bytes::(&pod.0); + assert_eq!( + value, + &CheckedBitPatternEnumNested::A(CheckedBitPatternCEnumWithFields::A( + 0xcc5555cc + )) + ); + + // next we'll check invalid first discriminant fails + let pod = Align8Bytes([ + 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, // byte 0 discriminant = 2 = invalid, bytes 1-7 padding + 0x00, 0x00, 0x00, 0x00, 0xcc, 0x55, 0x55, + 0xcc, // bytes 8-15 are the nested CheckedBitPatternCEnumWithFields = A, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // bytes 16-23 padding + ]); + let result = + bytemuck::checked::try_from_bytes::(&pod.0); + assert_eq!(result, Err(CheckedCastError::InvalidBitPattern)); + + // next we'll check variant B, nested variant B + let pod = Align8Bytes([ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, // byte 0 discriminant = 1 = variant B, bytes 1-7 padding + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, /* bytes 8-15 is C int size discriminant of + * CheckedBitPatternCDefaultDiscrimimantEnumWithFields, 1 (LE byte + * order) = variant B */ + 0xcc, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0xcc, // bytes 16-13 is the data contained in nested variant B + ]); + let value = + bytemuck::checked::from_bytes::(&pod.0); + assert_eq!( + value, + &CheckedBitPatternEnumNested::B( + CheckedBitPatternCDefaultDiscriminantEnumWithFields::B { + c: 0xcc555555555555cc + } + ) + ); + + // finally we'll check variant B, nested invalid discriminant + let pod = Align8Bytes([ + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, // 1 discriminant = variant B, bytes 1-7 padding + 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, /* bytes 8-15 is C int size discriminant of + * CheckedBitPatternCDefaultDiscrimimantEnumWithFields, 0x08 is + * invalid */ + 0xcc, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, + 0xcc, // bytes 16-13 is the data contained in nested variant B + ]); + let result = + bytemuck::checked::try_from_bytes::(&pod.0); + assert_eq!(result, Err(CheckedCastError::InvalidBitPattern)); +} +#[test] +fn checkedbitpattern_transparent_enum_with_fields() { + let pod = [0xcc, 0x55, 0x55, 0xcc]; + let value = bytemuck::checked::pod_read_unaligned::< + CheckedBitPatternTransparentEnumWithFields, + >(&pod); + assert_eq!( + value, + CheckedBitPatternTransparentEnumWithFields::A { b: 0xcc5555cc } + ); +} + +#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +#[repr(C, align(16))] +struct Issue127 {} + +use bytemuck as reexport_name; +#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable, bytemuck::ByteEq)] +#[bytemuck(crate = "reexport_name")] +#[repr(C)] +struct Issue93 {} diff --git a/third_party/rust/codespan-reporting/.cargo-checksum.json b/third_party/rust/codespan-reporting/.cargo-checksum.json index 643c4571a96f..2a36aa497d65 100644 --- a/third_party/rust/codespan-reporting/.cargo-checksum.json +++ b/third_party/rust/codespan-reporting/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"CHANGELOG.md":"8a2b57ab18c006ff09e7adf2ac1f49b669a9f909562d40dc7e02bdb0ddc4637a","Cargo.lock":"8934aeb5b0a6bbc9ebb2a1d12a356a52773d6fc8fb815d7bf9f40cd8ee631bbb","Cargo.toml":"e8a590d176da1a52d74f27019a351a5536c1530b7b39cbd03959e98bfebc72c1","examples/custom_files.rs":"9838cce42b692c81e6935faee23fb2d9b1dcef3d91fd62a1f2d0b1b4b25ca0e2","examples/peg_calculator.rs":"ed6ec885c18e266859df0727c94e98f8a214321336e915632189bee0a9af8f0f","examples/readme_preview.rs":"8fac03e3a58e09cbaf768ff247d895c3c71fa6efffae75b051e8fbfd1a1bea1b","examples/reusable_diagnostic.rs":"1950213ddd5e7e6c23689b786831807c6b43bdc2ed573852755831896ea524ed","examples/term.rs":"a5f0ea3a8a1d39ec6198c1e00511b25340787206df4ab08c9181949af6ee9cac","src/diagnostic.rs":"bf1e6b8ce3027f2d86bc391e6159b611d4964e5fd7f9fa532736599633934040","src/files.rs":"ef8eed9306a66d6c3bca7809c70259a68b53d1f5dab9647aa2cc0c101c5f77d2","src/lib.rs":"8d6b9662d6b8fb77fd141b79179d127e328f1162f666e5d8c9b6184501f471f6","src/term.rs":"4ceacd3cb4270083321f39cd2ec74fece79bd828db5e54810ebfe6e62a02f9d6","src/term/config.rs":"ac1b09ae8a6c52e6a8068271356cb869ee10aec186e14c12ee019bddd1d5f667","src/term/renderer.rs":"5277a686316fd79c364fbfb3a338593f7c1b6a57f23563f9b57ea66b8f55ca46","src/term/views.rs":"96f4d13623e496c2c5937a0776a180328b420c1267e5e23d2535d4643e2032db","tests/snapshots/term__empty__medium_color.snap":"538d6366898a43c9924418f3fe5ee279fae6130e829f3c20b8e981acac66458f","tests/snapshots/term__empty__medium_no_color.snap":"20fe2b034eda6304976d1aab98fd14b488be9a2a584a10a03cfe0fe1034bd112","tests/snapshots/term__empty__rich_ascii_no_color.snap":"fb421694bfc97340f353c0cdb1355afb759b28ac0e8d38df4cde840bced69616","tests/snapshots/term__empty__rich_color.snap":"bf40415825f8d2555e5f1f22d87f79c5815061d41f8ebe980766dc655d1eef6d","tests/snapshots/term__empty__rich_no_color.snap":"fb421694bfc97340f353c0cdb1355afb759b28ac0e8d38df4cde840bced69616","tests/snapshots/term__empty__short_color.snap":"538d6366898a43c9924418f3fe5ee279fae6130e829f3c20b8e981acac66458f","tests/snapshots/term__empty__short_no_color.snap":"20fe2b034eda6304976d1aab98fd14b488be9a2a584a10a03cfe0fe1034bd112","tests/snapshots/term__empty_ranges__medium_color.snap":"3e20d029efb6b26f4176604f55a481b52886bfd179910c6acff3f4e3b6b353b6","tests/snapshots/term__empty_ranges__medium_no_color.snap":"73fce8e0dedf4918f79eb2eaa267a554f10f87bfe4bb5b66df242fd38aa151f0","tests/snapshots/term__empty_ranges__rich_ascii_no_color.snap":"6044c49318c793a2c6075b665da93bedd5db649e79073054f6ed0b79d9df47d9","tests/snapshots/term__empty_ranges__rich_color.snap":"46d636261b92f635129391a7f9c0f3fddd1dc538819ce99aedf7c85e45a839f1","tests/snapshots/term__empty_ranges__rich_no_color.snap":"60dd9a44ce9a874a2eeea0bd4318f4c81c9995ba4ffe468978559a260abadec6","tests/snapshots/term__empty_ranges__short_color.snap":"3e20d029efb6b26f4176604f55a481b52886bfd179910c6acff3f4e3b6b353b6","tests/snapshots/term__empty_ranges__short_no_color.snap":"73fce8e0dedf4918f79eb2eaa267a554f10f87bfe4bb5b66df242fd38aa151f0","tests/snapshots/term__fizz_buzz__medium_color.snap":"7243f5311f35a1f0769eef74155c4dedc85b2504720b03ac93baf65b6a89cb52","tests/snapshots/term__fizz_buzz__medium_no_color.snap":"f08a0ac4e4c818d581c7ca84ea29fd29067ac07d19dabc007fb3b0af41245986","tests/snapshots/term__fizz_buzz__rich_ascii_no_color.snap":"c875d5c4b4bfb007bd4bcc99d94b09c726c866f70bda89e37a0077666ddd9b05","tests/snapshots/term__fizz_buzz__rich_color.snap":"55b4e35a2589670126d408dfa62c7ee885b7a60dc047fc0bb5ef92d6b31a3b1b","tests/snapshots/term__fizz_buzz__rich_no_color.snap":"9a0e5381e480a77d048172e393abb5bdb502165a89266378aeedfcb6e4938990","tests/snapshots/term__fizz_buzz__short_color.snap":"931f54fa6bce28ac654abc09b3974e1c1a45d2e07b42967a0b8b95b96781fbb9","tests/snapshots/term__fizz_buzz__short_no_color.snap":"b41b26cccea0cf312fbe31e8399f0d13e35fc73f03a0a431382b640cbf591f5b","tests/snapshots/term__message__medium_color.snap":"38521081a11e41868f724c91b0f2d8b70558afcc00e67349b568c2fe7f351b25","tests/snapshots/term__message__medium_no_color.snap":"fd55e2cbe67af1d3648eded4af9d0c34459acafa621cde774d1b9fc7c4aee94d","tests/snapshots/term__message__rich_ascii_no_color.snap":"9ef6ca322af9f0723b452e718ae43c9269e962519c16c5e2271ac5a98ec634f2","tests/snapshots/term__message__rich_color.snap":"0c737dab7f1b332c51c111bf4f7c9ec62e810817c5d62cbe2433677a1488826f","tests/snapshots/term__message__rich_no_color.snap":"9ef6ca322af9f0723b452e718ae43c9269e962519c16c5e2271ac5a98ec634f2","tests/snapshots/term__message__short_color.snap":"38521081a11e41868f724c91b0f2d8b70558afcc00e67349b568c2fe7f351b25","tests/snapshots/term__message__short_no_color.snap":"fd55e2cbe67af1d3648eded4af9d0c34459acafa621cde774d1b9fc7c4aee94d","tests/snapshots/term__message_and_notes__medium_color.snap":"662f6396dfc5b386d560b2aca9ac16106d7b14ee2abea981dd178c1ccbf2c256","tests/snapshots/term__message_and_notes__medium_no_color.snap":"44cc76150fa84908d77159635df98333f8aac85b4fb08e1815267176c953c518","tests/snapshots/term__message_and_notes__rich_ascii_no_color.snap":"8c3d999d7e08b898812f0a29999d6102a4a0781311a7b2e3e1c28493858d932e","tests/snapshots/term__message_and_notes__rich_color.snap":"e374968a58d73e564004f99ab5fade4f449fb0d1a8ec53ea523a131bcb62899c","tests/snapshots/term__message_and_notes__rich_no_color.snap":"8c3d999d7e08b898812f0a29999d6102a4a0781311a7b2e3e1c28493858d932e","tests/snapshots/term__message_and_notes__short_color.snap":"38521081a11e41868f724c91b0f2d8b70558afcc00e67349b568c2fe7f351b25","tests/snapshots/term__message_and_notes__short_no_color.snap":"fd55e2cbe67af1d3648eded4af9d0c34459acafa621cde774d1b9fc7c4aee94d","tests/snapshots/term__message_errorcode__rich_ascii_no_color.snap":"e70d0d4b9ac4a9498031afa3adddad19a901132b1e4c6535296e588a80bb73a7","tests/snapshots/term__message_errorcode__rich_no_color.snap":"e70d0d4b9ac4a9498031afa3adddad19a901132b1e4c6535296e588a80bb73a7","tests/snapshots/term__message_errorcode__short_no_color.snap":"a3cea73a584a91ae08cef8b4f17171c87aa643437cc6450f47fdf3e73d21df41","tests/snapshots/term__multifile__medium_color.snap":"3814c9f6e71e9bf5c31cb60be2311fd113ed77b2b86899e02f9e2b863f1df329","tests/snapshots/term__multifile__medium_no_color.snap":"e60241dd266446049e2a2fa77d46b13e04f8be10e971076606f8f9eef4457340","tests/snapshots/term__multifile__rich_ascii_no_color.snap":"8726c673ee427d9e13f374abec7fc6c0e37dde662012564f57be18d96d7cd39b","tests/snapshots/term__multifile__rich_color.snap":"ef7d6fe9fc97f8eafa51cb246f4b0ca53b61025aff5915bd86cc65bc813b6f6c","tests/snapshots/term__multifile__rich_no_color.snap":"96c58db425a2eba517c56690f2344396370784c2992581696f63be86a0621b39","tests/snapshots/term__multifile__short_color.snap":"cb654ad52babb928b20b1f6e3a7aee15f715282da789b4b961ef6ca6113324da","tests/snapshots/term__multifile__short_no_color.snap":"946d3eb63f7d8f1e00b4202db50c3c86b9d01452b3ec160c76b1655d31c843d6","tests/snapshots/term__multiline_omit__rich_no_color.snap":"c2c9a923d51551fcc777b7ebcf54f188a41855a62f939cfffaaebd87b8aaa55d","tests/snapshots/term__multiline_overlapping__medium_color.snap":"56ebba06bb5d4afbfd8fc7aa5e027910add76acbeda5da48e74da5218f95c2b2","tests/snapshots/term__multiline_overlapping__medium_no_color.snap":"149c321447cbd917c8c8bf42aca1310bc7a2f53a8df3efdecf0d30bde1ccc836","tests/snapshots/term__multiline_overlapping__rich_ascii_no_color.snap":"a32627a61a1e682e1a169ba46c50bca8d4292c6c253049891fcf048539507ab8","tests/snapshots/term__multiline_overlapping__rich_color.snap":"01f0a70739c03a738320848a579c6c74696d9e7afed4de3729e6b63cbda3e67c","tests/snapshots/term__multiline_overlapping__rich_no_color.snap":"729cdf6c159cc051354661973d81204360f6e46fa3848938aa4afd21135b8cea","tests/snapshots/term__multiline_overlapping__short_color.snap":"2ce68675d9ceb7b461a9e02eb39e3a196b9ec98027c9076adb91139711a6bacc","tests/snapshots/term__multiline_overlapping__short_no_color.snap":"fd1edf7cb7a6e02e514eac0f6231cdd36bec235b4ab3263f6454f6c756d3f825","tests/snapshots/term__overlapping__medium_color.snap":"5444be575c8640c58a6aa8b50dd64c8fc63594741defc9fa214c33b651dd4028","tests/snapshots/term__overlapping__medium_no_color.snap":"7fbad37e55a77d6ff97b64253ce26b3d78aff3fa3f3911e53d21cf1f5373e4d2","tests/snapshots/term__overlapping__rich_ascii_no_color.snap":"a8bc87ab545e17c8f2fd78f1efa4026ab62ad126e7f80dfebd3ce0ceb658c4c4","tests/snapshots/term__overlapping__rich_color.snap":"838c6be71d27dbf129651cf4634638112c45b143a0f65b6d6875eff45c94c2c8","tests/snapshots/term__overlapping__rich_no_color.snap":"8b0ae77b69f00245cea434b422ffe8e27f80ffca0010ed924f2ea3741f983947","tests/snapshots/term__overlapping__short_color.snap":"e38418c4f5652fb736446961bed9685151655704dcac12438ab153695a7730f4","tests/snapshots/term__overlapping__short_no_color.snap":"7714827d440e0ea3867d42d951eab1bead34b42c219baa9cb007ed8f553a1961","tests/snapshots/term__position_indicator__medium_no_color.snap":"cebbd419d8a627688f6a653eb7a819704a0149bade9b7580788d428f8eb33daf","tests/snapshots/term__position_indicator__rich_ascii_no_color.snap":"8ce0ba088cd72a1e2e32bc5025f9ed347afc71a7d105bd750165368d6dbafe65","tests/snapshots/term__position_indicator__rich_no_color.snap":"02c39a4bda3efda11d01e0dd48f56275b92894d2e9d8fa66b4f779bcb1fa4ff8","tests/snapshots/term__position_indicator__short_no_color.snap":"97719ff1de01f320490358ffded0953d2a96d79021b2d13b9995207d06e16d7f","tests/snapshots/term__same_line__medium_color.snap":"1313706a93ab35585c5cf290af342fa1e1906684dd4f1b0704a5e2f3ee724c96","tests/snapshots/term__same_line__medium_no_color.snap":"b918a8f898c8e6830d2a0ccf43761daf1ebc0699b4f51d6f096411e788a8b4fe","tests/snapshots/term__same_line__rich_ascii_no_color.snap":"8cde34de3f8ca4da2d286ec581ca1bc155ca422088523f91e83f6bf4f9405413","tests/snapshots/term__same_line__rich_color.snap":"fc093d51cb3dbeb4824b5c55cf224a0013a69313fbf3ce219074c58bf4dcef83","tests/snapshots/term__same_line__rich_no_color.snap":"c50495bc7da9757246a7d697d214961ed0a7bdd3ab275de67ba9e1a6fd39d2d4","tests/snapshots/term__same_line__short_color.snap":"ab9a0b24d0af89baea2d07930dc3e04c3537042ea61bcc1392940f995d3a8be5","tests/snapshots/term__same_line__short_no_color.snap":"ae9884e1a5bcec9260ea8139246726aba1e4c4029156c1ab50f1ccb5d63397f5","tests/snapshots/term__same_ranges__medium_color.snap":"58a870d2d20773d9516b23752ba112e2f95935dc18ae14215597f3d68963a181","tests/snapshots/term__same_ranges__medium_no_color.snap":"ec73645042c5e13a8bd1eed26837dba4c286e6780acffc218a0f423c0b993210","tests/snapshots/term__same_ranges__rich_ascii_no_color.snap":"d805c605c1ca56f39d41bb4150713d3f850ca63924a770d1d72b6230753b0b4a","tests/snapshots/term__same_ranges__rich_color.snap":"56f2fa66f1074fb167f1c8e61133a445267f6915c94f7331e9a68d553ebcb796","tests/snapshots/term__same_ranges__rich_no_color.snap":"34b82a6ce9ccbd08e4e964c78eb27f765e30f0198616ab5d4f15f02a62d2e37e","tests/snapshots/term__same_ranges__short_color.snap":"d5b7bf052b326f38ea81bd9ca970a409cda8c9445d800c1468ddc452782e1626","tests/snapshots/term__same_ranges__short_no_color.snap":"16dfc93660a12f0687c0335924469266d6cf803aef34f72bdcf0e3d4b55a902a","tests/snapshots/term__tab_columns__tab_width_2_no_color.snap":"3fa128b8c18d87cdb59001dedb79ed059ca1927a24fce60c10b8139ee6ae38d1","tests/snapshots/term__tab_columns__tab_width_3_no_color.snap":"793e99ef600f3da03b13b50d5c44859ed9dd32400affd0a0100c0155c3fdca72","tests/snapshots/term__tab_columns__tab_width_6_no_color.snap":"93a92a73b3ca3e517d5a4e4e215579dada60ff95af79c0c6d8b1116ba75130e0","tests/snapshots/term__tab_columns__tab_width_default_no_color.snap":"ec082ca97f84631c19fc139f615bc0da588cd5fe706ba7307dd80bac3db20cb2","tests/snapshots/term__tabbed__tab_width_3_no_color.snap":"f8e7318f381ef21526cb9b9f459be404e8e7ee3abbb7174f8a45e3bb2ae81ae4","tests/snapshots/term__tabbed__tab_width_6_no_color.snap":"0a4ece01e179e1b8b7b13d1789d391761ced6c45c459dbaef3d8256793864578","tests/snapshots/term__tabbed__tab_width_default_no_color.snap":"f77441824f2f327d559f70e56973d7798a42e20de302ed351d68a28bc6f05b10","tests/snapshots/term__unicode__medium_no_color.snap":"3faa0672b2c04198b0268a6f0266606dfeee56e58373b478d886d503bc17678a","tests/snapshots/term__unicode__rich_no_color.snap":"60c5045f536d26f99cbd48e818405d04b992ee1fcb6898aedeabddc626e01b47","tests/snapshots/term__unicode__short_no_color.snap":"b7c0ac33ac0cb30fe5f8dca0cd6f6fe43c707cc96eaef11b177e08a91859e9d2","tests/snapshots/term__unicode_spans__medium_no_color.snap":"07e2dc69276e03efeb44f903a731604ce04cf2d2a26f4d482e1622a7d26c3e15","tests/snapshots/term__unicode_spans__rich_no_color.snap":"2a89e43664d367fedf343e2de070e5719a9de69c8806b5b5edec6d854c63a9c9","tests/snapshots/term__unicode_spans__short_no_color.snap":"07e2dc69276e03efeb44f903a731604ce04cf2d2a26f4d482e1622a7d26c3e15","tests/support/color_buffer.rs":"8de39e81ab157ae18edb9ca8b2c015fee248e15624dc172e593354b43d3897bd","tests/support/mod.rs":"c708541328abf4a613b9d67333e0b88cd91ddb42968002ce515b0b851d12e516","tests/term.rs":"8b9189738b21d664ba2c2b8ab34e05420b6aae01bed35703bcd5d3a73e0f8689"},"package":"3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"} \ No newline at end of file +{"files":{"CHANGELOG.md":"55013a57c4a1bbb0ad3f1a3510484675298a9095eac539f0bc4be6fe3c2a9415","Cargo.lock":"2c65d911d06ee02469e0362309e81b37eda353b4114b1b19bdeab1aae972cff9","Cargo.toml":"9cfae523922bd40225f58e5bfeb1899433c91f71efb05d2a6054d7e612bd0674","LICENSE":"c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4","README.md":"b3425f80b0908f476be186268e70a7107c759cb89b4567a8d9c459aafeea215d","examples/custom_files.rs":"4d209529f00b67010293717520b7006d814f396bd25ba1c02703a2d4717d0328","examples/peg_calculator.rs":"4b45499dba04e422bfc293c72662adf8d76fbbcca4663881d45873550cd87795","examples/readme_preview.rs":"75eb2f2707e9659815eeb7ed7674bdd3a7d87c1a715f8bdeb9cb8824a782705e","examples/reusable_diagnostic.rs":"a187f3e5021f77320ef14195b20b490da03e754559c547228abab728308b5970","examples/term.rs":"0f304bc6904ea0597da924c095c8bb6db5e033cf66ad6db10561c0d79d602c05","src/diagnostic.rs":"0dc7510d4cb765945a650b9c82359b449264ecbc32c4bfe55ec514fbfbfbddd1","src/files.rs":"5f3ac0c882d46d7fe18f354b22c8d479fa1da3a6ed5a4438bd9c59bf9a1cf7e6","src/lib.rs":"3a9fd046e578e4b149e6623e4f475490a29f8e2a2aea8081b4738b1d68bf93a6","src/term.rs":"0a60ef79a8ddbbc09004fd21c340a8b4cc70a4e2ee4f9520c3bfccc023522123","src/term/config.rs":"c194bcd92800c95319d55b6bcca73ffc9cf27ddbe2e1ce55362e7f1c0b58e5eb","src/term/renderer.rs":"2c3446c47dc2250ebc2a6aa53acb3dbf7e8dcd85f2d6117454003bffbff8b274","src/term/views.rs":"e96f8bba37c8d5d76109e6323055954737ac6cfbe596839711d0a8baf0a2b090","tests/snapshots/term__empty__medium_color.snap":"538d6366898a43c9924418f3fe5ee279fae6130e829f3c20b8e981acac66458f","tests/snapshots/term__empty__medium_no_color.snap":"20fe2b034eda6304976d1aab98fd14b488be9a2a584a10a03cfe0fe1034bd112","tests/snapshots/term__empty__rich_ascii_no_color.snap":"fb421694bfc97340f353c0cdb1355afb759b28ac0e8d38df4cde840bced69616","tests/snapshots/term__empty__rich_color.snap":"bf40415825f8d2555e5f1f22d87f79c5815061d41f8ebe980766dc655d1eef6d","tests/snapshots/term__empty__rich_no_color.snap":"fb421694bfc97340f353c0cdb1355afb759b28ac0e8d38df4cde840bced69616","tests/snapshots/term__empty__short_color.snap":"538d6366898a43c9924418f3fe5ee279fae6130e829f3c20b8e981acac66458f","tests/snapshots/term__empty__short_no_color.snap":"20fe2b034eda6304976d1aab98fd14b488be9a2a584a10a03cfe0fe1034bd112","tests/snapshots/term__empty_ranges__medium_color.snap":"3e20d029efb6b26f4176604f55a481b52886bfd179910c6acff3f4e3b6b353b6","tests/snapshots/term__empty_ranges__medium_no_color.snap":"73fce8e0dedf4918f79eb2eaa267a554f10f87bfe4bb5b66df242fd38aa151f0","tests/snapshots/term__empty_ranges__rich_ascii_no_color.snap":"6044c49318c793a2c6075b665da93bedd5db649e79073054f6ed0b79d9df47d9","tests/snapshots/term__empty_ranges__rich_color.snap":"46d636261b92f635129391a7f9c0f3fddd1dc538819ce99aedf7c85e45a839f1","tests/snapshots/term__empty_ranges__rich_no_color.snap":"60dd9a44ce9a874a2eeea0bd4318f4c81c9995ba4ffe468978559a260abadec6","tests/snapshots/term__empty_ranges__short_color.snap":"3e20d029efb6b26f4176604f55a481b52886bfd179910c6acff3f4e3b6b353b6","tests/snapshots/term__empty_ranges__short_no_color.snap":"73fce8e0dedf4918f79eb2eaa267a554f10f87bfe4bb5b66df242fd38aa151f0","tests/snapshots/term__fizz_buzz__medium_color.snap":"7243f5311f35a1f0769eef74155c4dedc85b2504720b03ac93baf65b6a89cb52","tests/snapshots/term__fizz_buzz__medium_no_color.snap":"f08a0ac4e4c818d581c7ca84ea29fd29067ac07d19dabc007fb3b0af41245986","tests/snapshots/term__fizz_buzz__rich_ascii_no_color.snap":"c875d5c4b4bfb007bd4bcc99d94b09c726c866f70bda89e37a0077666ddd9b05","tests/snapshots/term__fizz_buzz__rich_color.snap":"55b4e35a2589670126d408dfa62c7ee885b7a60dc047fc0bb5ef92d6b31a3b1b","tests/snapshots/term__fizz_buzz__rich_no_color.snap":"9a0e5381e480a77d048172e393abb5bdb502165a89266378aeedfcb6e4938990","tests/snapshots/term__fizz_buzz__short_color.snap":"931f54fa6bce28ac654abc09b3974e1c1a45d2e07b42967a0b8b95b96781fbb9","tests/snapshots/term__fizz_buzz__short_no_color.snap":"b41b26cccea0cf312fbe31e8399f0d13e35fc73f03a0a431382b640cbf591f5b","tests/snapshots/term__message__medium_color.snap":"38521081a11e41868f724c91b0f2d8b70558afcc00e67349b568c2fe7f351b25","tests/snapshots/term__message__medium_no_color.snap":"fd55e2cbe67af1d3648eded4af9d0c34459acafa621cde774d1b9fc7c4aee94d","tests/snapshots/term__message__rich_ascii_no_color.snap":"9ef6ca322af9f0723b452e718ae43c9269e962519c16c5e2271ac5a98ec634f2","tests/snapshots/term__message__rich_color.snap":"0c737dab7f1b332c51c111bf4f7c9ec62e810817c5d62cbe2433677a1488826f","tests/snapshots/term__message__rich_no_color.snap":"9ef6ca322af9f0723b452e718ae43c9269e962519c16c5e2271ac5a98ec634f2","tests/snapshots/term__message__short_color.snap":"38521081a11e41868f724c91b0f2d8b70558afcc00e67349b568c2fe7f351b25","tests/snapshots/term__message__short_no_color.snap":"fd55e2cbe67af1d3648eded4af9d0c34459acafa621cde774d1b9fc7c4aee94d","tests/snapshots/term__message_and_notes__medium_color.snap":"662f6396dfc5b386d560b2aca9ac16106d7b14ee2abea981dd178c1ccbf2c256","tests/snapshots/term__message_and_notes__medium_no_color.snap":"44cc76150fa84908d77159635df98333f8aac85b4fb08e1815267176c953c518","tests/snapshots/term__message_and_notes__rich_ascii_no_color.snap":"8c3d999d7e08b898812f0a29999d6102a4a0781311a7b2e3e1c28493858d932e","tests/snapshots/term__message_and_notes__rich_color.snap":"e374968a58d73e564004f99ab5fade4f449fb0d1a8ec53ea523a131bcb62899c","tests/snapshots/term__message_and_notes__rich_no_color.snap":"8c3d999d7e08b898812f0a29999d6102a4a0781311a7b2e3e1c28493858d932e","tests/snapshots/term__message_and_notes__short_color.snap":"38521081a11e41868f724c91b0f2d8b70558afcc00e67349b568c2fe7f351b25","tests/snapshots/term__message_and_notes__short_no_color.snap":"fd55e2cbe67af1d3648eded4af9d0c34459acafa621cde774d1b9fc7c4aee94d","tests/snapshots/term__message_errorcode__rich_ascii_no_color.snap":"e70d0d4b9ac4a9498031afa3adddad19a901132b1e4c6535296e588a80bb73a7","tests/snapshots/term__message_errorcode__rich_no_color.snap":"e70d0d4b9ac4a9498031afa3adddad19a901132b1e4c6535296e588a80bb73a7","tests/snapshots/term__message_errorcode__short_no_color.snap":"a3cea73a584a91ae08cef8b4f17171c87aa643437cc6450f47fdf3e73d21df41","tests/snapshots/term__multifile__medium_color.snap":"3814c9f6e71e9bf5c31cb60be2311fd113ed77b2b86899e02f9e2b863f1df329","tests/snapshots/term__multifile__medium_no_color.snap":"e60241dd266446049e2a2fa77d46b13e04f8be10e971076606f8f9eef4457340","tests/snapshots/term__multifile__rich_ascii_no_color.snap":"8726c673ee427d9e13f374abec7fc6c0e37dde662012564f57be18d96d7cd39b","tests/snapshots/term__multifile__rich_color.snap":"ef7d6fe9fc97f8eafa51cb246f4b0ca53b61025aff5915bd86cc65bc813b6f6c","tests/snapshots/term__multifile__rich_no_color.snap":"96c58db425a2eba517c56690f2344396370784c2992581696f63be86a0621b39","tests/snapshots/term__multifile__short_color.snap":"cb654ad52babb928b20b1f6e3a7aee15f715282da789b4b961ef6ca6113324da","tests/snapshots/term__multifile__short_no_color.snap":"946d3eb63f7d8f1e00b4202db50c3c86b9d01452b3ec160c76b1655d31c843d6","tests/snapshots/term__multiline_omit__rich_no_color.snap":"c2c9a923d51551fcc777b7ebcf54f188a41855a62f939cfffaaebd87b8aaa55d","tests/snapshots/term__multiline_overlapping__medium_color.snap":"56ebba06bb5d4afbfd8fc7aa5e027910add76acbeda5da48e74da5218f95c2b2","tests/snapshots/term__multiline_overlapping__medium_no_color.snap":"149c321447cbd917c8c8bf42aca1310bc7a2f53a8df3efdecf0d30bde1ccc836","tests/snapshots/term__multiline_overlapping__rich_ascii_no_color.snap":"a32627a61a1e682e1a169ba46c50bca8d4292c6c253049891fcf048539507ab8","tests/snapshots/term__multiline_overlapping__rich_color.snap":"01f0a70739c03a738320848a579c6c74696d9e7afed4de3729e6b63cbda3e67c","tests/snapshots/term__multiline_overlapping__rich_no_color.snap":"729cdf6c159cc051354661973d81204360f6e46fa3848938aa4afd21135b8cea","tests/snapshots/term__multiline_overlapping__short_color.snap":"2ce68675d9ceb7b461a9e02eb39e3a196b9ec98027c9076adb91139711a6bacc","tests/snapshots/term__multiline_overlapping__short_no_color.snap":"fd1edf7cb7a6e02e514eac0f6231cdd36bec235b4ab3263f6454f6c756d3f825","tests/snapshots/term__overlapping__medium_color.snap":"5444be575c8640c58a6aa8b50dd64c8fc63594741defc9fa214c33b651dd4028","tests/snapshots/term__overlapping__medium_no_color.snap":"7fbad37e55a77d6ff97b64253ce26b3d78aff3fa3f3911e53d21cf1f5373e4d2","tests/snapshots/term__overlapping__rich_ascii_no_color.snap":"a8bc87ab545e17c8f2fd78f1efa4026ab62ad126e7f80dfebd3ce0ceb658c4c4","tests/snapshots/term__overlapping__rich_color.snap":"838c6be71d27dbf129651cf4634638112c45b143a0f65b6d6875eff45c94c2c8","tests/snapshots/term__overlapping__rich_no_color.snap":"8b0ae77b69f00245cea434b422ffe8e27f80ffca0010ed924f2ea3741f983947","tests/snapshots/term__overlapping__short_color.snap":"e38418c4f5652fb736446961bed9685151655704dcac12438ab153695a7730f4","tests/snapshots/term__overlapping__short_no_color.snap":"7714827d440e0ea3867d42d951eab1bead34b42c219baa9cb007ed8f553a1961","tests/snapshots/term__position_indicator__medium_no_color.snap":"cebbd419d8a627688f6a653eb7a819704a0149bade9b7580788d428f8eb33daf","tests/snapshots/term__position_indicator__rich_ascii_no_color.snap":"8ce0ba088cd72a1e2e32bc5025f9ed347afc71a7d105bd750165368d6dbafe65","tests/snapshots/term__position_indicator__rich_no_color.snap":"02c39a4bda3efda11d01e0dd48f56275b92894d2e9d8fa66b4f779bcb1fa4ff8","tests/snapshots/term__position_indicator__short_no_color.snap":"97719ff1de01f320490358ffded0953d2a96d79021b2d13b9995207d06e16d7f","tests/snapshots/term__same_line__medium_color.snap":"1313706a93ab35585c5cf290af342fa1e1906684dd4f1b0704a5e2f3ee724c96","tests/snapshots/term__same_line__medium_no_color.snap":"b918a8f898c8e6830d2a0ccf43761daf1ebc0699b4f51d6f096411e788a8b4fe","tests/snapshots/term__same_line__rich_ascii_no_color.snap":"8cde34de3f8ca4da2d286ec581ca1bc155ca422088523f91e83f6bf4f9405413","tests/snapshots/term__same_line__rich_color.snap":"fc093d51cb3dbeb4824b5c55cf224a0013a69313fbf3ce219074c58bf4dcef83","tests/snapshots/term__same_line__rich_no_color.snap":"c50495bc7da9757246a7d697d214961ed0a7bdd3ab275de67ba9e1a6fd39d2d4","tests/snapshots/term__same_line__short_color.snap":"ab9a0b24d0af89baea2d07930dc3e04c3537042ea61bcc1392940f995d3a8be5","tests/snapshots/term__same_line__short_no_color.snap":"ae9884e1a5bcec9260ea8139246726aba1e4c4029156c1ab50f1ccb5d63397f5","tests/snapshots/term__same_ranges__medium_color.snap":"58a870d2d20773d9516b23752ba112e2f95935dc18ae14215597f3d68963a181","tests/snapshots/term__same_ranges__medium_no_color.snap":"ec73645042c5e13a8bd1eed26837dba4c286e6780acffc218a0f423c0b993210","tests/snapshots/term__same_ranges__rich_ascii_no_color.snap":"d805c605c1ca56f39d41bb4150713d3f850ca63924a770d1d72b6230753b0b4a","tests/snapshots/term__same_ranges__rich_color.snap":"56f2fa66f1074fb167f1c8e61133a445267f6915c94f7331e9a68d553ebcb796","tests/snapshots/term__same_ranges__rich_no_color.snap":"34b82a6ce9ccbd08e4e964c78eb27f765e30f0198616ab5d4f15f02a62d2e37e","tests/snapshots/term__same_ranges__short_color.snap":"d5b7bf052b326f38ea81bd9ca970a409cda8c9445d800c1468ddc452782e1626","tests/snapshots/term__same_ranges__short_no_color.snap":"16dfc93660a12f0687c0335924469266d6cf803aef34f72bdcf0e3d4b55a902a","tests/snapshots/term__surrounding_lines__rich_no_color.snap":"45f405ed9eca0ff9ee638917b5b82a5bd54bf92c477bd20a7bf256cf7b795e8a","tests/snapshots/term__tab_columns__tab_width_2_no_color.snap":"3fa128b8c18d87cdb59001dedb79ed059ca1927a24fce60c10b8139ee6ae38d1","tests/snapshots/term__tab_columns__tab_width_3_no_color.snap":"793e99ef600f3da03b13b50d5c44859ed9dd32400affd0a0100c0155c3fdca72","tests/snapshots/term__tab_columns__tab_width_6_no_color.snap":"93a92a73b3ca3e517d5a4e4e215579dada60ff95af79c0c6d8b1116ba75130e0","tests/snapshots/term__tab_columns__tab_width_default_no_color.snap":"ec082ca97f84631c19fc139f615bc0da588cd5fe706ba7307dd80bac3db20cb2","tests/snapshots/term__tabbed__tab_width_3_no_color.snap":"f8e7318f381ef21526cb9b9f459be404e8e7ee3abbb7174f8a45e3bb2ae81ae4","tests/snapshots/term__tabbed__tab_width_6_no_color.snap":"0a4ece01e179e1b8b7b13d1789d391761ced6c45c459dbaef3d8256793864578","tests/snapshots/term__tabbed__tab_width_default_no_color.snap":"f77441824f2f327d559f70e56973d7798a42e20de302ed351d68a28bc6f05b10","tests/snapshots/term__unicode__medium_no_color.snap":"3faa0672b2c04198b0268a6f0266606dfeee56e58373b478d886d503bc17678a","tests/snapshots/term__unicode__rich_no_color.snap":"60c5045f536d26f99cbd48e818405d04b992ee1fcb6898aedeabddc626e01b47","tests/snapshots/term__unicode__short_no_color.snap":"b7c0ac33ac0cb30fe5f8dca0cd6f6fe43c707cc96eaef11b177e08a91859e9d2","tests/snapshots/term__unicode_spans__medium_no_color.snap":"07e2dc69276e03efeb44f903a731604ce04cf2d2a26f4d482e1622a7d26c3e15","tests/snapshots/term__unicode_spans__rich_no_color.snap":"2a89e43664d367fedf343e2de070e5719a9de69c8806b5b5edec6d854c63a9c9","tests/snapshots/term__unicode_spans__short_no_color.snap":"07e2dc69276e03efeb44f903a731604ce04cf2d2a26f4d482e1622a7d26c3e15","tests/support/color_buffer.rs":"8b07b5f094730ca78d6ccbe8d7380ad45cf5fcdbdb7a4e3886411b9b78e283f9","tests/support/mod.rs":"e6cd56465fa779e3b57b10719fb7a663ddbb26cead0f99fc2ebbc18fc3a36c37","tests/term.rs":"e0eca175442c86ae5785a070ad0cf7fac14daa960968ece12c1971eae2f69539"},"package":"fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81"} \ No newline at end of file diff --git a/third_party/rust/codespan-reporting/CHANGELOG.md b/third_party/rust/codespan-reporting/CHANGELOG.md index 68109e06653f..2284e483fece 100644 --- a/third_party/rust/codespan-reporting/CHANGELOG.md +++ b/third_party/rust/codespan-reporting/CHANGELOG.md @@ -7,6 +7,64 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +The minimum supported rustc version is now `1.67.0` (was `1.40.0`). +This is because some dependencies now require this Rust version. + +### Added + +- Add the option to display a configurable amount of lines in front of and after any label. +- The `Severity` enum now implements full `Ord`. (#335) + +### Changed + +- Broken lines are now rendered properly with multiline spans. + + We used to render the wrong lines in the gutter when there were multiline spans + and there were breaks in the file. + +
+ Example + + Before: + + ```text + error[0001]: oh noes, a cupcake has occurred! + ┌─ test:4:1 + │ + 4 │ ╭ Cupcake ipsum dolor. Sit amet marshmallow topping cheesecake + 5 │ │ muffin. Halvah croissant candy canes bonbon candy. Apple pie jelly + │ ╭─│─────────' + · │ + 10 │ │ │ Muffin danish chocolate soufflé pastry icing bonbon oat cake. + 11 │ │ │ Powder cake jujubes oat cake. Lemon drops tootsie roll marshmallow + │ │ ╰─────────────────────────────' blah blah + · │ │ + 19 │ │ soufflé marzipan. Chocolate bar oat cake jujubes lollipop pastry + 20 │ │ cupcake. Candy canes cupcake toffee gingerbread candy canes muffin + │ ╰──────────' blah blah + ``` + + After: + + ```text + error[0001]: oh noes, a cupcake has occurred! + ┌─ test:4:1 + │ + 4 │ ╭ Cupcake ipsum dolor. Sit amet marshmallow topping cheesecake + 5 │ │ muffin. Halvah croissant candy canes bonbon candy. Apple pie jelly + │ ╭─│─────────' + · │ │ + 10 │ │ │ Muffin danish chocolate soufflé pastry icing bonbon oat cake. + 11 │ │ │ Powder cake jujubes oat cake. Lemon drops tootsie roll marshmallow + │ │ ╰─────────────────────────────' blah blah + · │ + 19 │ │ soufflé marzipan. Chocolate bar oat cake jujubes lollipop pastry + 20 │ │ cupcake. Candy canes cupcake toffee gingerbread candy canes muffin + │ ╰──────────' blah blah + ``` + +
+ ## [0.11.1] - 2021-01-18 ### Added diff --git a/third_party/rust/codespan-reporting/Cargo.lock b/third_party/rust/codespan-reporting/Cargo.lock index ff92d96df3ed..ebf052f22947 100644 --- a/third_party/rust/codespan-reporting/Cargo.lock +++ b/third_party/rust/codespan-reporting/Cargo.lock @@ -1,42 +1,30 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -[[package]] -name = "ansi_term" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -dependencies = [ - "winapi", -] +version = 3 [[package]] name = "anyhow" -version = "1.0.38" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afddf7f520a80dbf76e6f50a35bca42a2331ef227a28b3b6dc5c2e2338d114b1" - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] +checksum = "a9ff2deb543832ee7b1a08060c38cc6af5816e96d3fcb6fc2e99bd15634e5c7f" [[package]] name = "bitflags" -version = "1.2.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" [[package]] name = "cc" -version = "1.0.67" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c69b077ad434294d3ce9f1f6143a2a4b89a8a2d54ef813d85003a4fd1137fd" +checksum = "7db2f146208d7e0fbee761b09cd65a7f51ccc38705d4e7262dad4d73b12a76b1" [[package]] name = "cfg-if" @@ -50,32 +38,17 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "clap" -version = "2.33.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" -dependencies = [ - "ansi_term", - "atty", - "bitflags", - "strsim", - "textwrap", - "unicode-width", - "vec_map", -] - [[package]] name = "codespan-reporting" -version = "0.11.1" +version = "0.12.0" dependencies = [ "anyhow", "insta", "lazy_static", "peg", + "pico-args", "rustyline", "serde", - "structopt", "termcolor", "unicode-width", "unindent", @@ -95,20 +68,20 @@ dependencies = [ ] [[package]] -name = "dirs-next" -version = "1.0.2" +name = "dirs" +version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf36e65a80337bea855cd4ef9b8401ffce06a7baedf2e85ec467b1ac3f6e82b6" +checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" dependencies = [ - "cfg-if 1.0.0", - "dirs-sys-next", + "cfg-if 0.1.10", + "dirs-sys", ] [[package]] -name = "dirs-sys-next" -version = "0.1.2" +name = "dirs-sys" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", "redox_users", @@ -117,45 +90,27 @@ dependencies = [ [[package]] name = "dtoa" -version = "0.4.7" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88d7ed2934d741c6b37e33e3832298e8850b53fd2d2bea03873375596c7cea4e" +checksum = "5edd69c67b2f8e0911629b7e6b8a34cb3956613cd7c6e6414966dee349c2db4f" [[package]] name = "encode_unicode" -version = "0.3.6" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +checksum = "8927c1166f271b9c3950500ffada737e31321b43fba0ac2100d954974d2020b5" [[package]] name = "getrandom" -version = "0.2.2" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8" +checksum = "190092ea657667030ac6a35e305e62fc4dd69fd98ac98631e5d3a2b1575a12b5" dependencies = [ "cfg-if 1.0.0", "libc", "wasi", ] -[[package]] -name = "heck" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cbf45460356b7deeb5e3415b5563308c0a9b057c85e12b06ad551f98d0a6ac" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "hermit-abi" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c" -dependencies = [ - "libc", -] - [[package]] name = "insta" version = "1.6.3" @@ -173,9 +128,9 @@ dependencies = [ [[package]] name = "itoa" -version = "0.4.7" +version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd25036021b0de88a0aff6b850051563c6516d0bf53f8638938edbb9de732736" +checksum = "1306f3464951f30e30d12373d31c79fbd52d236e5e896fd92f96ec7babbbe60b" [[package]] name = "lazy_static" @@ -185,15 +140,26 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.86" +version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7282d924be3275cec7f6756ff4121987bc6481325397dde6ba3e7802b1a8b1c" +checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" + +[[package]] +name = "libredox" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85c833ca1e66078851dba29046874e38f08b2c883700aa29a03ddd3b23814ee8" +dependencies = [ + "bitflags 2.4.2", + "libc", + "redox_syscall", +] [[package]] name = "linked-hash-map" -version = "0.5.4" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" +checksum = "8dd5a6d5999d9907cda8ed67bbd137d3af8085216c2ac62de5be860bd41f304a" [[package]] name = "log" @@ -206,27 +172,31 @@ dependencies = [ [[package]] name = "memchr" -version = "2.3.4" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" - -[[package]] -name = "nix" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83450fe6a6142ddd95fb064b746083fc4ef1705fe81f64a64e1d4b39f54a1055" +checksum = "e01e64d9017d18e7fc09d8e4fe0e28ff6931019e979fb8019319db7ca827f8a6" dependencies = [ - "bitflags", - "cc", - "cfg-if 0.1.10", "libc", ] [[package]] -name = "peg" -version = "0.6.3" +name = "nix" +version = "0.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f76678828272f177ac33b7e2ac2e3e73cc6c1cd1e3e387928aa69562fa51367" +checksum = "6c722bee1037d430d0f8e687bbdbf222f27cc6e4e68d5caf630857bb2b6dbdce" +dependencies = [ + "bitflags 1.3.2", + "cc", + "cfg-if 0.1.10", + "libc", + "void", +] + +[[package]] +name = "peg" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07c0b841ea54f523f7aa556956fbd293bcbe06f2e67d2eb732b7278aaf1d166a" dependencies = [ "peg-macros", "peg-runtime", @@ -234,9 +204,9 @@ dependencies = [ [[package]] name = "peg-macros" -version = "0.6.3" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "636d60acf97633e48d266d7415a9355d4389cea327a193f87df395d88cd2b14d" +checksum = "b5aa52829b8decbef693af90202711348ab001456803ba2a98eb4ec8fb70844c" dependencies = [ "peg-runtime", "proc-macro2", @@ -245,84 +215,66 @@ dependencies = [ [[package]] name = "peg-runtime" -version = "0.6.3" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555b1514d2d99d78150d3c799d4c357a3e2c2a8062cd108e93a06d9057629c5" +checksum = "c719dcf55f09a3a7e764c6649ab594c18a177e3599c467983cdf644bfc0a4088" [[package]] -name = "proc-macro-error" -version = "1.0.4" +name = "pico-args" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] +checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315" [[package]] name = "proc-macro2" -version = "1.0.24" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ - "unicode-xid", + "unicode-ident", ] [[package]] name = "quote" -version = "1.0.9" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] [[package]] name = "redox_syscall" -version = "0.2.5" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9" +checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" dependencies = [ - "bitflags", + "bitflags 1.3.2", ] [[package]] name = "redox_users" -version = "0.4.0" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64" +checksum = "a18479200779601e498ada4e8c1e1f50e3ee19deb0259c25825a98b5603b2cb4" dependencies = [ "getrandom", - "redox_syscall", + "libredox", + "thiserror", ] [[package]] name = "rustyline" -version = "6.3.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0d5e7b0219a3eadd5439498525d4765c59b7c993ef0c12244865cd2d988413" +checksum = "de64be8eecbe428b6924f1d8430369a01719fbb182c26fa431ddbb0a95f5315d" dependencies = [ "cfg-if 0.1.10", - "dirs-next", + "dirs", "libc", "log", "memchr", "nix", - "scopeguard", "unicode-segmentation", "unicode-width", "utf8parse", @@ -331,30 +283,24 @@ dependencies = [ [[package]] name = "ryu" -version = "1.0.5" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "c92464b447c0ee8c4fb3824ecc8383b81717b9f1e74ba2e72540aef7b9f82997" [[package]] name = "serde" -version = "1.0.123" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d5161132722baa40d802cc70b15262b98258453e85e5d1d365c757c73869ae" +checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.123" +version = "1.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9391c295d64fc0abb2c556bad848f33cb8296276b1ad2677d1ae1ace4f258f31" +checksum = "cbd1ae72adb44aab48f325a02444a5fc079349a8d804c1fc922aed3f7454c74e" dependencies = [ "proc-macro2", "quote", @@ -363,9 +309,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.62" +version = "1.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea1c6153794552ea7cf7cf63b1231a25de00ec90db326ba6264440fa08e31486" +checksum = "dcac07dbffa1c65e7f816ab9eba78eb142c6d44410f4eeba1e26e4f5dfa56b95" dependencies = [ "itoa", "ryu", @@ -374,9 +320,9 @@ dependencies = [ [[package]] name = "serde_yaml" -version = "0.8.17" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15654ed4ab61726bf918a39cb8d98a2e2995b002387807fa6ba58fdf7f59bb23" +checksum = "f7baae0a99f1a324984bcdc5f0718384c1f69775f1c7eec8b859b71b443e3fd7" dependencies = [ "dtoa", "linked-hash-map", @@ -390,134 +336,109 @@ version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a04629d2e8ecdcf30e0188e3699ed6d50d5750d0219db146a790065fe92a897" -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - -[[package]] -name = "structopt" -version = "0.3.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5277acd7ee46e63e5168a80734c9f6ee81b1367a7d8772a2d765df2a3705d28c" -dependencies = [ - "clap", - "lazy_static", - "structopt-derive", -] - -[[package]] -name = "structopt-derive" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ba9cdfda491b814720b6b06e0cac513d922fc407582032e8706e9f137976f90" -dependencies = [ - "heck", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "syn" -version = "1.0.60" +version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c700597eca8a5a762beb35753ef6b94df201c81cca676604f547495a0d7f0081" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", - "unicode-xid", + "unicode-ident", ] [[package]] name = "termcolor" -version = "1.1.2" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" dependencies = [ "winapi-util", ] [[package]] name = "terminal_size" -version = "0.1.16" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ca8ced750734db02076f44132d802af0b33b09942331f4459dde8636fd2406" +checksum = "5e288a11a53da73056d3c7cbdee83b1d1ce3344c3210a1f8b56c32f9662a87e9" dependencies = [ "libc", "winapi", ] [[package]] -name = "textwrap" -version = "0.11.0" +name = "thiserror" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +checksum = "a5ab016db510546d856297882807df8da66a16fb8c4101cb8b30054b0d5b2d9c" dependencies = [ - "unicode-width", + "thiserror-impl", ] [[package]] -name = "unicode-segmentation" -version = "1.7.1" +name = "thiserror-impl" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0d2e7be6ae3a5fa87eed5fb451aff96f2573d2694942e40543ae0bbe19c796" +checksum = "5420d42e90af0c38c3290abcca25b9b3bdf379fc9f55c528f53a269d9c9a267e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-segmentation" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8083c594e02b8ae1654ae26f0ade5158b119bd88ad0e8227a5d8fcd72407946" [[package]] name = "unicode-width" -version = "0.1.8" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" - -[[package]] -name = "unicode-xid" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" [[package]] name = "unindent" -version = "0.1.7" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f14ee04d9415b52b3aeab06258a3f07093182b88ba0f9b8d203f211a7a7d41c7" +checksum = "3508be0ce1bacc38d579b69bffb4b8d469f5af0c388ff4890b2b294e61671ffe" [[package]] name = "utf8parse" -version = "0.2.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372" +checksum = "8772a4ccbb4e89959023bc5b7cb8623a795caa7092d99f3aa9501b9484d4557d" [[package]] name = "uuid" -version = "0.8.2" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7" +checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" [[package]] -name = "vec_map" -version = "0.8.2" +name = "void" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - -[[package]] -name = "version_check" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" [[package]] name = "wasi" -version = "0.10.2+wasi-snapshot-preview1" +version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "winapi" -version = "0.3.9" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +checksum = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", @@ -531,9 +452,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] @@ -546,9 +467,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "yaml-rust" -version = "0.4.5" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85" +checksum = "57ab38ee1a4a266ed033496cf9af1828d8d6e6c1cfa5f643a2809effcae4d628" dependencies = [ "linked-hash-map", ] diff --git a/third_party/rust/codespan-reporting/Cargo.toml b/third_party/rust/codespan-reporting/Cargo.toml index 5a3d588b3a62..47b1d786fa98 100644 --- a/third_party/rust/codespan-reporting/Cargo.toml +++ b/third_party/rust/codespan-reporting/Cargo.toml @@ -3,35 +3,89 @@ # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies +# to registry (e.g., crates.io) dependencies. # -# If you believe there's an error in this file please file an -# issue against the rust-lang/cargo repository. If you're -# editing this file be aware that the upstream Cargo.toml -# will likely look very different (and much more reasonable) +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. [package] -edition = "2018" +edition = "2021" +rust-version = "1.67" name = "codespan-reporting" -version = "0.11.1" +version = "0.12.0" authors = ["Brendan Zabarauskas "] +build = false exclude = ["assets/**"] +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false description = "Beautiful diagnostic reporting for text-based programming languages" homepage = "https://github.com/brendanzab/codespan" documentation = "https://docs.rs/codespan-reporting" -readme = "../README.md" +readme = "README.md" license = "Apache-2.0" repository = "https://github.com/brendanzab/codespan" + +[features] +ascii-only = [] +default = [ + "std", + "termcolor", +] +serialization = ["serde"] +std = ["serde?/std"] +termcolor = [ + "std", + "dep:termcolor", +] + +[lib] +name = "codespan_reporting" +path = "src/lib.rs" + +[[example]] +name = "custom_files" +path = "examples/custom_files.rs" + +[[example]] +name = "peg_calculator" +path = "examples/peg_calculator.rs" + +[[example]] +name = "readme_preview" +path = "examples/readme_preview.rs" + +[[example]] +name = "reusable_diagnostic" +path = "examples/reusable_diagnostic.rs" + +[[example]] +name = "term" +path = "examples/term.rs" + +[[test]] +name = "term" +path = "tests/term.rs" + [dependencies.serde] version = "1" -features = ["derive"] +features = [ + "derive", + "alloc", +] optional = true +default-features = false [dependencies.termcolor] -version = "1" +version = "1.0.4" +optional = true [dependencies.unicode-width] -version = "0.1" +version = ">=0.1,<0.3" + [dev-dependencies.anyhow] version = "1" @@ -42,17 +96,18 @@ version = "1.6.3" version = "1.4" [dev-dependencies.peg] -version = "0.6" +version = "0.7" + +[dev-dependencies.pico-args] +version = "0.5.0" [dev-dependencies.rustyline] version = "6" -[dev-dependencies.structopt] -version = "0.3" - [dev-dependencies.unindent] version = "0.1" -[features] -ascii-only = [] -serialization = ["serde", "serde/rc"] +[lints.clippy] +alloc_instead_of_core = "warn" +std_instead_of_alloc = "warn" +std_instead_of_core = "warn" diff --git a/third_party/rust/codespan-reporting/LICENSE b/third_party/rust/codespan-reporting/LICENSE new file mode 100644 index 000000000000..261eeb9e9f8b --- /dev/null +++ b/third_party/rust/codespan-reporting/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/third_party/rust/codespan-reporting/README.md b/third_party/rust/codespan-reporting/README.md new file mode 100644 index 000000000000..c76495306ea1 --- /dev/null +++ b/third_party/rust/codespan-reporting/README.md @@ -0,0 +1,152 @@ +# codespan-reporting + +[![Continuous integration][actions-badge]][actions-url] +[![Crates.io][crate-badge]][crate-url] +[![Docs.rs][docs-badge]][docs-url] +[![Matrix][matrix-badge]][matrix-lobby] + +[actions-badge]: https://img.shields.io/github/actions/workflow/status/brendanzab/codespan/ci.yml?branch=master +[actions-url]: https://github.com/brendanzab/codespan/actions +[crate-url]: https://crates.io/crates/codespan-reporting +[crate-badge]: https://img.shields.io/crates/v/codespan-reporting.svg +[docs-url]: https://docs.rs/codespan-reporting +[docs-badge]: https://docs.rs/codespan-reporting/badge.svg +[matrix-badge]: https://img.shields.io/badge/matrix-%23codespan%3Amatrix.org-blue.svg +[matrix-lobby]: https://app.element.io/#/room/#codespan:matrix.org + +Beautiful diagnostic reporting for text-based programming languages. + +![Example preview](./codespan-reporting/assets/readme_preview.svg?sanitize=true) + +Languages like Rust and Elm already support beautiful error reporting output, +but it can take a significant amount work to implement this for new programming +languages! The `codespan-reporting` crate aims to make beautiful error +diagnostics easy and relatively painless for everyone! + +We're still working on improving the crate to help it support broader use cases, +and improving the quality of the diagnostic rendering, so stay tuned for +updates and please give us feedback if you have it. Contributions are also very +welcome! + +## Example + +```rust +use codespan_reporting::diagnostic::{Diagnostic, Label}; +use codespan_reporting::files::SimpleFiles; +use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; + +// `files::SimpleFile` and `files::SimpleFiles` help you get up and running with +// `codespan-reporting` quickly! More complicated use cases can be supported +// by creating custom implementations of the `files::Files` trait. + +let mut files = SimpleFiles::new(); + +let file_id = files.add( + "FizzBuzz.fun", + unindent::unindent( + r#" + module FizzBuzz where + + fizz₁ : Nat → String + fizz₁ num = case (mod num 5) (mod num 3) of + 0 0 => "FizzBuzz" + 0 _ => "Fizz" + _ 0 => "Buzz" + _ _ => num + + fizz₂ : Nat → String + fizz₂ num = + case (mod num 5) (mod num 3) of + 0 0 => "FizzBuzz" + 0 _ => "Fizz" + _ 0 => "Buzz" + _ _ => num + "#, + ), +); + +// We normally recommend creating a custom diagnostic data type for your +// application, and then converting that to `codespan-reporting`'s diagnostic +// type, but for the sake of this example we construct it directly. + +let diagnostic = Diagnostic::error() + .with_message("`case` clauses have incompatible types") + .with_code("E0308") + .with_labels(vec![ + Label::primary(file_id, 328..331).with_message("expected `String`, found `Nat`"), + Label::secondary(file_id, 211..331).with_message("`case` clauses have incompatible types"), + Label::secondary(file_id, 258..268).with_message("this is found to be of type `String`"), + Label::secondary(file_id, 284..290).with_message("this is found to be of type `String`"), + Label::secondary(file_id, 306..312).with_message("this is found to be of type `String`"), + Label::secondary(file_id, 186..192).with_message("expected type `String` found here"), + ]) + .with_notes(vec![unindent::unindent( + " + expected type `String` + found type `Nat` + ", + )]); + +// We now set up the writer and configuration, and then finally render the +// diagnostic to standard error. + +let writer = StandardStream::stderr(ColorChoice::Always); +let config = codespan_reporting::term::Config::default(); + +term::emit(&mut writer.lock(), &config, &files, &diagnostic)?; +``` + +## Running the CLI example + +To get an idea of what the colored CLI output looks like, +clone the [repository](https://github.com/brendanzab/codespan) +and run the following shell command: + +```sh +cargo run --example term +``` + +More examples of using `codespan-reporting` can be found in the +[examples directory](./codespan-reporting/examples). + +## Projects using codespan-reporting + +`codespan-reporting` is currently used in the following projects: + +- [cargo-deny](https://github.com/EmbarkStudios/cargo-deny) +- [cargo-about](https://github.com/EmbarkStudios/cargo-about) +- [CXX](https://github.com/dtolnay/cxx) +- [full_moon](https://github.com/Kampfkarren/full-moon) +- [Gleam](https://github.com/gleam-lang/gleam) +- [Gluon](https://github.com/gluon-lang/gluon) +- [MDBook LinkCheck](https://github.com/Michael-F-Bryan/mdbook-linkcheck) +- [mos](https://github.com/datatrash/mos) +- [Pikelet](https://github.com/pikelet-lang/pikelet) +- [Naga](https://github.com/gfx-rs/wgpu/tree/trunk/naga) +- [Spade](https://gitlab.com/spade-lang/spade) + + ... [any many more](https://crates.io/crates/codespan-reporting/reverse_dependencies) + +## Alternatives to codespan-reporting + +There are a number of alternatives to `codespan-reporting`, including: + +- [annotate-snippets][annotate-snippets] +- [codemap][codemap] +- [language-reporting][language-reporting] (a fork of codespan) + +These are all ultimately inspired by rustc's excellent [error reporting infrastructure][librustc_errors]. + +[annotate-snippets]: https://crates.io/crates/annotate-snippets +[codemap]: https://crates.io/crates/codemap +[language-reporting]: https://crates.io/crates/language-reporting +[librustc_errors]: https://github.com/rust-lang/rust/tree/master/compiler/rustc_errors/src + +## Contributing + +A guide to contributing to codespan-reporting [can be found here](/CONTRIBUTING.md). + +## Code of Conduct + +Please note that this project is released with a [Code of Conduct](./CODE_OF_CONDUCT.md). +By participating in this project you agree to abide by its terms. diff --git a/third_party/rust/codespan-reporting/examples/custom_files.rs b/third_party/rust/codespan-reporting/examples/custom_files.rs index dbbacda59120..c61a51500d9f 100644 --- a/third_party/rust/codespan-reporting/examples/custom_files.rs +++ b/third_party/rust/codespan-reporting/examples/custom_files.rs @@ -12,7 +12,7 @@ use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::term; use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; -use std::ops::Range; +use core::ops::Range; fn main() -> anyhow::Result<()> { let mut files = files::Files::new(); @@ -42,7 +42,7 @@ fn main() -> anyhow::Result<()> { /// A module containing the file implementation mod files { use codespan_reporting::files; - use std::ops::Range; + use core::ops::Range; /// A file that is backed by an `Arc`. #[derive(Debug, Clone)] @@ -57,14 +57,13 @@ mod files { impl File { fn line_start(&self, line_index: usize) -> Result { - use std::cmp::Ordering; + use core::cmp::Ordering; match line_index.cmp(&self.line_starts.len()) { - Ordering::Less => Ok(self + Ordering::Less => Ok(*self .line_starts .get(line_index) - .expect("failed despite previous check") - .clone()), + .expect("failed despite previous check")), Ordering::Equal => Ok(self.source.len()), Ordering::Greater => Err(files::Error::LineTooLarge { given: line_index, @@ -96,7 +95,7 @@ mod files { name: impl Into, source: impl Into, ) -> Option { - use std::convert::TryFrom; + use core::convert::TryFrom; let file_id = FileId(u32::try_from(self.files.len()).ok()?); let name = name.into(); diff --git a/third_party/rust/codespan-reporting/examples/peg_calculator.rs b/third_party/rust/codespan-reporting/examples/peg_calculator.rs index 882ce6da8c49..5975c82aff87 100644 --- a/third_party/rust/codespan-reporting/examples/peg_calculator.rs +++ b/third_party/rust/codespan-reporting/examples/peg_calculator.rs @@ -14,6 +14,7 @@ use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; use rustyline::error::ReadlineError; use rustyline::Editor; +#[allow(clippy::redundant_closure_call)] peg::parser! { grammar arithmetic() for str { rule number() -> i64 diff --git a/third_party/rust/codespan-reporting/examples/readme_preview.rs b/third_party/rust/codespan-reporting/examples/readme_preview.rs index 2cf96f60cbfe..be419b108ccd 100644 --- a/third_party/rust/codespan-reporting/examples/readme_preview.rs +++ b/third_party/rust/codespan-reporting/examples/readme_preview.rs @@ -9,30 +9,43 @@ use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::files::SimpleFile; -use codespan_reporting::term::termcolor::{Color, ColorSpec, StandardStream, WriteColor}; -use codespan_reporting::term::{self, ColorArg}; +use codespan_reporting::term::termcolor::{ + Color, ColorChoice, ColorSpec, StandardStream, WriteColor, +}; +use codespan_reporting::term::{self}; use std::io::{self, Write}; -use structopt::StructOpt; -#[derive(Debug, StructOpt)] -#[structopt(name = "emit")] +#[derive(Debug)] pub enum Opts { /// Render SVG output Svg, /// Render Stderr output Stderr { /// Configure coloring of output - #[structopt( - long = "color", - parse(try_from_str), - default_value = "auto", - possible_values = ColorArg::VARIANTS, - case_insensitive = true - )] - color: ColorArg, + color: ColorChoice, }, } +fn parse_args() -> Result { + let mut pargs = pico_args::Arguments::from_env(); + match pargs.subcommand()? { + Some(value) => match value.as_str() { + "svg" => Ok(Opts::Svg), + "stderr" => { + let color = pargs + .opt_value_from_str("--color")? + .unwrap_or(ColorChoice::Auto); + Ok(Opts::Stderr { color }) + } + _ => Err(pico_args::Error::Utf8ArgumentParsingFailed { + value, + cause: "not a valid subcommand".to_owned(), + }), + }, + None => Err(pico_args::Error::MissingArgument), + } +} + fn main() -> anyhow::Result<()> { let file = SimpleFile::new( "FizzBuzz.fun", @@ -76,18 +89,18 @@ fn main() -> anyhow::Result<()> { ", )])]; - // let mut files = SimpleFiles::new(); - match Opts::from_args() { + match parse_args()? { Opts::Svg => { let mut buffer = Vec::new(); let mut writer = HtmlEscapeWriter::new(SvgWriter::new(&mut buffer)); let config = codespan_reporting::term::Config { + #[cfg(feature = "termcolor")] styles: codespan_reporting::term::Styles::with_blue(Color::Blue), ..codespan_reporting::term::Config::default() }; for diagnostic in &diagnostics { - term::emit(&mut writer, &config, &file, &diagnostic)?; + term::emit(&mut writer, &config, &file, diagnostic)?; } let num_lines = buffer.iter().filter(|byte| **byte == b'\n').count() + 1; @@ -175,10 +188,10 @@ fn main() -> anyhow::Result<()> { )?; } Opts::Stderr { color } => { - let writer = StandardStream::stderr(color.into()); + let writer = StandardStream::stderr(color); let config = codespan_reporting::term::Config::default(); for diagnostic in &diagnostics { - term::emit(&mut writer.lock(), &config, &file, &diagnostic)?; + term::emit(&mut writer.lock(), &config, &file, diagnostic)?; } } } @@ -294,7 +307,7 @@ impl WriteColor for SvgWriter { } Ok(false) - }; + } fn write_color(color: &Color, writer: &mut SvgWriter) -> io::Result<()> { match color { @@ -309,7 +322,7 @@ impl WriteColor for SvgWriter { // TODO: other colors _ => Ok(()), } - }; + } if let Some(fg) = spec.fg() { first = write_first(first, self)?; diff --git a/third_party/rust/codespan-reporting/examples/reusable_diagnostic.rs b/third_party/rust/codespan-reporting/examples/reusable_diagnostic.rs index d05dee854937..d1ec55fa8c0e 100644 --- a/third_party/rust/codespan-reporting/examples/reusable_diagnostic.rs +++ b/third_party/rust/codespan-reporting/examples/reusable_diagnostic.rs @@ -1,20 +1,20 @@ use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::files::SimpleFile; -use codespan_reporting::term::termcolor::StandardStream; -use codespan_reporting::term::{self, ColorArg}; -use std::ops::Range; -use structopt::StructOpt; +use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; +use codespan_reporting::term::{self}; +use core::ops::Range; -#[derive(Debug, StructOpt)] -#[structopt(name = "emit")] +#[derive(Debug)] pub struct Opts { - #[structopt(long = "color", - parse(try_from_str), - default_value = "auto", - possible_values = ColorArg::VARIANTS, - case_insensitive = true - )] - color: ColorArg, + color: ColorChoice, +} + +fn parse_args() -> Result { + let mut pargs = pico_args::Arguments::from_env(); + let color = pargs + .opt_value_from_str("--color")? + .unwrap_or(ColorChoice::Auto); + Ok(Opts { color }) } fn main() -> anyhow::Result<()> { @@ -38,8 +38,8 @@ fn main() -> anyhow::Result<()> { Error::MutatingImmutable(Item::new(20..23, "foo"), Item::new(51..59, "foo += 1")), ]; - let opts = Opts::from_args(); - let writer = StandardStream::stderr(opts.color.into()); + let Opts { color } = parse_args()?; + let writer = StandardStream::stderr(color); let config = codespan_reporting::term::Config::default(); for diagnostic in errors.iter().map(Error::report) { term::emit(&mut writer.lock(), &config, &file, &diagnostic)?; diff --git a/third_party/rust/codespan-reporting/examples/term.rs b/third_party/rust/codespan-reporting/examples/term.rs index 19bf8503c015..7fa12e9d2db7 100644 --- a/third_party/rust/codespan-reporting/examples/term.rs +++ b/third_party/rust/codespan-reporting/examples/term.rs @@ -7,26 +7,26 @@ use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::files::SimpleFiles; -use codespan_reporting::term::termcolor::StandardStream; -use codespan_reporting::term::{self, ColorArg}; -use structopt::StructOpt; +use codespan_reporting::term; +use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; -#[derive(Debug, StructOpt)] -#[structopt(name = "emit")] +#[derive(Debug)] pub struct Opts { /// Configure coloring of output - #[structopt( - long = "color", - parse(try_from_str), - default_value = "auto", - possible_values = ColorArg::VARIANTS, - case_insensitive = true - )] - pub color: ColorArg, + pub color: ColorChoice, +} + +fn parse_args() -> Result { + let mut pargs = pico_args::Arguments::from_env(); + let color = pargs + .opt_value_from_str("--color")? + .unwrap_or(ColorChoice::Auto); + Ok(Opts { color }) } fn main() -> anyhow::Result<()> { - let opts = Opts::from_args(); + let Opts { color } = parse_args()?; + let mut files = SimpleFiles::new(); let file_id1 = files.add( @@ -165,10 +165,10 @@ fn main() -> anyhow::Result<()> { )]), ]; - let writer = StandardStream::stderr(opts.color.into()); + let writer = StandardStream::stderr(color); let config = codespan_reporting::term::Config::default(); for diagnostic in &diagnostics { - term::emit(&mut writer.lock(), &config, &files, &diagnostic)?; + term::emit(&mut writer.lock(), &config, &files, diagnostic)?; } Ok(()) diff --git a/third_party/rust/codespan-reporting/src/diagnostic.rs b/third_party/rust/codespan-reporting/src/diagnostic.rs index c1f98bd435e4..1b19f4b2d7c7 100644 --- a/third_party/rust/codespan-reporting/src/diagnostic.rs +++ b/third_party/rust/codespan-reporting/src/diagnostic.rs @@ -1,8 +1,13 @@ //! Diagnostic data structures. +use alloc::{ + string::{String, ToString}, + vec::Vec, +}; +use core::ops::Range; + #[cfg(feature = "serialization")] use serde::{Deserialize, Serialize}; -use std::ops::Range; /// A severity level for diagnostic messages. /// @@ -16,38 +21,19 @@ use std::ops::Range; /// assert!(Severity::Warning > Severity::Note); /// assert!(Severity::Note > Severity::Help); /// ``` -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +#[derive(Copy, Clone, Hash, Debug, PartialEq, Eq, PartialOrd, Ord)] #[cfg_attr(feature = "serialization", derive(Serialize, Deserialize))] pub enum Severity { - /// An unexpected bug. - Bug, - /// An error. - Error, - /// A warning. - Warning, - /// A note. - Note, /// A help message. Help, -} - -impl Severity { - /// We want bugs to be the maximum severity, errors next, etc... - fn to_cmp_int(self) -> u8 { - match self { - Severity::Bug => 5, - Severity::Error => 4, - Severity::Warning => 3, - Severity::Note => 2, - Severity::Help => 1, - } - } -} - -impl PartialOrd for Severity { - fn partial_cmp(&self, other: &Severity) -> Option { - u8::partial_cmp(&self.to_cmp_int(), &other.to_cmp_int()) - } + /// A note. + Note, + /// A warning. + Warning, + /// An error. + Error, + /// An unexpected bug. + Bug, } #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd)] @@ -104,8 +90,8 @@ impl Label { } /// Add a message to the diagnostic. - pub fn with_message(mut self, message: impl Into) -> Label { - self.message = message.into(); + pub fn with_message(mut self, message: impl ToString) -> Label { + self.message = message.to_string(); self } } @@ -184,14 +170,20 @@ impl Diagnostic { } /// Set the error code of the diagnostic. - pub fn with_code(mut self, code: impl Into) -> Diagnostic { - self.code = Some(code.into()); + pub fn with_code(mut self, code: impl ToString) -> Diagnostic { + self.code = Some(code.to_string()); self } /// Set the message of the diagnostic. - pub fn with_message(mut self, message: impl Into) -> Diagnostic { - self.message = message.into(); + pub fn with_message(mut self, message: impl ToString) -> Diagnostic { + self.message = message.to_string(); + self + } + + /// Add a label to the diagnostic. + pub fn with_label(mut self, label: Label) -> Diagnostic { + self.labels.push(label); self } @@ -201,9 +193,33 @@ impl Diagnostic { self } + /// Add some labels to the diagnostic. + pub fn with_labels_iter( + mut self, + labels: impl IntoIterator>, + ) -> Diagnostic { + self.labels.extend(labels); + self + } + + /// Add a note to the diagnostic. + pub fn with_note(mut self, note: impl ToString) -> Diagnostic { + self.notes.push(note.to_string()); + self + } + /// Add some notes to the diagnostic. pub fn with_notes(mut self, mut notes: Vec) -> Diagnostic { self.notes.append(&mut notes); self } + + /// Add some notes to the diagnostic. + pub fn with_notes_iter( + mut self, + notes: impl IntoIterator, + ) -> Diagnostic { + self.notes.extend(notes); + self + } } diff --git a/third_party/rust/codespan-reporting/src/files.rs b/third_party/rust/codespan-reporting/src/files.rs index b25cd7978893..457fdac4a726 100644 --- a/third_party/rust/codespan-reporting/src/files.rs +++ b/third_party/rust/codespan-reporting/src/files.rs @@ -23,7 +23,14 @@ //! //! [`salsa`]: https://crates.io/crates/salsa -use std::ops::Range; +use alloc::vec::Vec; +use core::ops::Range; + +#[cfg(feature = "std")] +use std::error; + +#[cfg(not(feature = "std"))] +use core::error; /// An enum representing an error that happened while looking up a file or a piece of content in that file. #[derive(Debug)] @@ -40,17 +47,27 @@ pub enum Error { /// The given index is contained in the file, but is not a boundary of a UTF-8 code point. InvalidCharBoundary { given: usize }, /// There was a error while doing IO. + #[cfg(feature = "std")] Io(std::io::Error), + /// There was a error during formatting. + FormatError, } +#[cfg(feature = "std")] impl From for Error { fn from(err: std::io::Error) -> Error { Error::Io(err) } } -impl std::fmt::Display for Error { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { +impl From for Error { + fn from(_err: core::fmt::Error) -> Error { + Error::FormatError + } +} + +impl core::fmt::Display for Error { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Error::FileMissing => write!(f, "file missing"), Error::IndexTooLarge { given, max } => { @@ -63,14 +80,17 @@ impl std::fmt::Display for Error { write!(f, "invalid column {}, maximum column {}", given, max) } Error::InvalidCharBoundary { .. } => write!(f, "index is not a code point boundary"), + #[cfg(feature = "std")] Error::Io(err) => write!(f, "{}", err), + Error::FormatError => write!(f, "formatting error"), } } } -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { +impl error::Error for Error { + fn source(&self) -> Option<&(dyn error::Error + 'static)> { match &self { + #[cfg(feature = "std")] Error::Io(err) => Some(err), _ => None, } @@ -87,7 +107,7 @@ pub trait Files<'a> { /// for rendering `diagnostic::Label`s in the corresponding source files. type FileId: 'a + Copy + PartialEq; /// The user-facing name of a file, to be displayed in diagnostics. - type Name: 'a + std::fmt::Display; + type Name: 'a + core::fmt::Display; /// The source code of a file. type Source: 'a + AsRef; @@ -204,7 +224,7 @@ pub struct Location { /// assert_eq!(files::column_index(source, 2..13, 2 + 12), 3); /// ``` pub fn column_index(source: &str, line_range: Range, byte_index: usize) -> usize { - let end_index = std::cmp::min(byte_index, std::cmp::min(line_range.end, source.len())); + let end_index = core::cmp::min(byte_index, core::cmp::min(line_range.end, source.len())); (line_range.start..end_index) .filter(|byte_index| source.is_char_boundary(byte_index + 1)) @@ -248,8 +268,8 @@ pub fn column_index(source: &str, line_range: Range, byte_index: usize) - /// assert_eq!(line_index(&line_starts, 5), Some(1)); /// ``` // NOTE: this is copied in `codespan::file::line_starts` and should be kept in sync. -pub fn line_starts<'source>(source: &'source str) -> impl 'source + Iterator { - std::iter::once(0).chain(source.match_indices('\n').map(|(i, _)| i + 1)) +pub fn line_starts(source: &str) -> impl '_ + Iterator { + core::iter::once(0).chain(source.match_indices('\n').map(|(i, _)| i + 1)) } /// A file database that contains a single source file. @@ -272,7 +292,7 @@ pub struct SimpleFile { impl SimpleFile where - Name: std::fmt::Display, + Name: core::fmt::Display, Source: AsRef, { /// Create a new source file. @@ -297,7 +317,7 @@ where /// Return the starting byte index of the line with the specified line index. /// Convenience method that already generates errors if necessary. fn line_start(&self, line_index: usize) -> Result { - use std::cmp::Ordering; + use core::cmp::Ordering; match line_index.cmp(&self.line_starts.len()) { Ordering::Less => Ok(self @@ -316,7 +336,7 @@ where impl<'a, Name, Source> Files<'a> for SimpleFile where - Name: 'a + std::fmt::Display + Clone, + Name: 'a + core::fmt::Display + Clone, Source: 'a + AsRef, { type FileId = (); @@ -351,14 +371,14 @@ where /// This is useful for simple language tests, but it might be worth creating a /// custom implementation when a language scales beyond a certain size. /// It is a glorified `Vec` that implements the `Files` trait. -#[derive(Debug, Clone)] +#[derive(Debug, Default, Clone)] pub struct SimpleFiles { files: Vec>, } impl SimpleFiles where - Name: std::fmt::Display, + Name: core::fmt::Display, Source: AsRef, { /// Create a new files database. @@ -382,7 +402,7 @@ where impl<'a, Name, Source> Files<'a> for SimpleFiles where - Name: 'a + std::fmt::Display + Clone, + Name: 'a + core::fmt::Display + Clone, Source: 'a + AsRef, { type FileId = usize; diff --git a/third_party/rust/codespan-reporting/src/lib.rs b/third_party/rust/codespan-reporting/src/lib.rs index 28d7f24deb8b..76af4fd42e5b 100644 --- a/third_party/rust/codespan-reporting/src/lib.rs +++ b/third_party/rust/codespan-reporting/src/lib.rs @@ -1,6 +1,12 @@ //! Diagnostic reporting support for the codespan crate. #![forbid(unsafe_code)] +#![no_std] + +extern crate alloc; + +#[cfg(feature = "std")] +extern crate std; pub mod diagnostic; pub mod files; diff --git a/third_party/rust/codespan-reporting/src/term.rs b/third_party/rust/codespan-reporting/src/term.rs index 59baeb04e125..388db11622b7 100644 --- a/third_party/rust/codespan-reporting/src/term.rs +++ b/third_party/rust/codespan-reporting/src/term.rs @@ -1,7 +1,7 @@ //! Terminal back-end for emitting diagnostics. -use std::str::FromStr; -use termcolor::{ColorChoice, WriteColor}; +#[cfg(feature = "termcolor")] +use termcolor::WriteColor; use crate::diagnostic::Diagnostic; use crate::files::Files; @@ -10,73 +10,13 @@ mod config; mod renderer; mod views; +#[cfg(feature = "termcolor")] pub use termcolor; -pub use self::config::{Chars, Config, DisplayStyle, Styles}; +pub use self::config::{Chars, Config, DisplayStyle}; -/// A command line argument that configures the coloring of the output. -/// -/// This can be used with command line argument parsers like [`clap`] or [`structopt`]. -/// -/// [`clap`]: https://crates.io/crates/clap -/// [`structopt`]: https://crates.io/crates/structopt -/// -/// # Example -/// -/// ```rust -/// use codespan_reporting::term::termcolor::StandardStream; -/// use codespan_reporting::term::ColorArg; -/// use structopt::StructOpt; -/// -/// #[derive(Debug, StructOpt)] -/// #[structopt(name = "groovey-app")] -/// pub struct Opts { -/// /// Configure coloring of output -/// #[structopt( -/// long = "color", -/// default_value = "auto", -/// possible_values = ColorArg::VARIANTS, -/// case_insensitive = true, -/// )] -/// pub color: ColorArg, -/// } -/// -/// let opts = Opts::from_args(); -/// let writer = StandardStream::stderr(opts.color.into()); -/// ``` -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct ColorArg(pub ColorChoice); - -impl ColorArg { - /// Allowed values the argument. - /// - /// This is useful for generating documentation via [`clap`] or `structopt`'s - /// `possible_values` configuration. - /// - /// [`clap`]: https://crates.io/crates/clap - /// [`structopt`]: https://crates.io/crates/structopt - pub const VARIANTS: &'static [&'static str] = &["auto", "always", "ansi", "never"]; -} - -impl FromStr for ColorArg { - type Err = &'static str; - - fn from_str(src: &str) -> Result { - match src { - _ if src.eq_ignore_ascii_case("auto") => Ok(ColorArg(ColorChoice::Auto)), - _ if src.eq_ignore_ascii_case("always") => Ok(ColorArg(ColorChoice::Always)), - _ if src.eq_ignore_ascii_case("ansi") => Ok(ColorArg(ColorChoice::AlwaysAnsi)), - _ if src.eq_ignore_ascii_case("never") => Ok(ColorArg(ColorChoice::Never)), - _ => Err("valid values: auto, always, ansi, never"), - } - } -} - -impl Into for ColorArg { - fn into(self) -> ColorChoice { - self.0 - } -} +#[cfg(feature = "termcolor")] +pub use self::config::Styles; /// Emit a diagnostic using the given writer, context, config, and files. /// @@ -84,8 +24,10 @@ impl Into for ColorArg { /// * a file was removed from the file database. /// * a file was changed so that it is too small to have an index /// * IO fails -pub fn emit<'files, F: Files<'files>>( - writer: &mut dyn WriteColor, +pub fn emit<'files, F: Files<'files> + ?Sized>( + #[cfg(feature = "termcolor")] writer: &mut dyn WriteColor, + #[cfg(all(not(feature = "termcolor"), feature = "std"))] writer: &mut dyn std::io::Write, + #[cfg(all(not(feature = "termcolor"), not(feature = "std")))] writer: &mut dyn core::fmt::Write, config: &Config, files: &'files F, diagnostic: &Diagnostic, @@ -101,8 +43,10 @@ pub fn emit<'files, F: Files<'files>>( } } -#[cfg(test)] +#[cfg(all(test, feature = "termcolor"))] mod tests { + use alloc::{vec, vec::Vec}; + use super::*; use crate::diagnostic::Label; diff --git a/third_party/rust/codespan-reporting/src/term/config.rs b/third_party/rust/codespan-reporting/src/term/config.rs index c92a6b60479a..597c755d064f 100644 --- a/third_party/rust/codespan-reporting/src/term/config.rs +++ b/third_party/rust/codespan-reporting/src/term/config.rs @@ -1,6 +1,10 @@ -use termcolor::{Color, ColorSpec}; +use alloc::string::String; -use crate::diagnostic::{LabelStyle, Severity}; +#[cfg(feature = "termcolor")] +use { + crate::diagnostic::{LabelStyle, Severity}, + termcolor::{Color, ColorSpec}, +}; /// Configures how a diagnostic is rendered. #[derive(Clone, Debug)] @@ -14,6 +18,7 @@ pub struct Config { /// Defaults to: `4`. pub tab_width: usize, /// Styles to use when rendering the diagnostic. + #[cfg(feature = "termcolor")] pub styles: Styles, /// Characters to use when rendering the diagnostic. pub chars: Chars, @@ -29,6 +34,14 @@ pub struct Config { /// /// [`Label`]: crate::diagnostic::Label pub end_context_lines: usize, + /// The minimum number of lines before a label that should be included for context. + /// + /// Defaults to: `0`. + pub before_label_lines: usize, + /// The minimum number of lines after a label that should be included for context. + /// + /// Defaults to: `0`. + pub after_label_lines: usize, } impl Default for Config { @@ -36,10 +49,13 @@ impl Default for Config { Config { display_style: DisplayStyle::Rich, tab_width: 4, + #[cfg(feature = "termcolor")] styles: Styles::default(), chars: Chars::default(), start_context_lines: 3, end_context_lines: 1, + before_label_lines: 0, + after_label_lines: 0, } } } @@ -83,6 +99,7 @@ pub enum DisplayStyle { } /// Styles to use when rendering the diagnostic. +#[cfg(feature = "termcolor")] #[derive(Clone, Debug)] pub struct Styles { /// The style to use when rendering bug headers. @@ -134,6 +151,7 @@ pub struct Styles { pub note_bullet: ColorSpec, } +#[cfg(feature = "termcolor")] impl Styles { /// The style used to mark a header at a given severity. pub fn header(&self, severity: Severity) -> &ColorSpec { @@ -184,6 +202,7 @@ impl Styles { } } +#[cfg(feature = "termcolor")] impl Default for Styles { fn default() -> Styles { // Blue is really difficult to see on the standard windows command line diff --git a/third_party/rust/codespan-reporting/src/term/renderer.rs b/third_party/rust/codespan-reporting/src/term/renderer.rs index eeb8965d25c1..ac2c005c8e4a 100644 --- a/third_party/rust/codespan-reporting/src/term/renderer.rs +++ b/third_party/rust/codespan-reporting/src/term/renderer.rs @@ -1,10 +1,21 @@ -use std::io::{self, Write}; -use std::ops::Range; -use termcolor::{ColorSpec, WriteColor}; +use alloc::string::String; +use core::ops::Range; use crate::diagnostic::{LabelStyle, Severity}; use crate::files::{Error, Location}; -use crate::term::{Chars, Config, Styles}; +use crate::term::{Chars, Config}; + +#[cfg(feature = "termcolor")] +use { + crate::term::Styles, + termcolor::{ColorSpec, WriteColor}, +}; + +#[cfg(feature = "std")] +use std::io::{self, Write}; + +#[cfg(not(feature = "std"))] +use core::fmt::{Arguments, Write}; /// The 'location focus' of a source code snippet. pub struct Locus { @@ -109,14 +120,18 @@ type Underline = (LabelStyle, VerticalBound); /// /// Filler text from http://www.cupcakeipsum.com pub struct Renderer<'writer, 'config> { + #[cfg(feature = "termcolor")] writer: &'writer mut dyn WriteColor, + #[cfg(not(feature = "termcolor"))] + writer: &'writer mut dyn Write, config: &'config Config, } impl<'writer, 'config> Renderer<'writer, 'config> { /// Construct a renderer from the given writer and config. pub fn new( - writer: &'writer mut dyn WriteColor, + #[cfg(feature = "termcolor")] writer: &'writer mut dyn WriteColor, + #[cfg(not(feature = "termcolor"))] writer: &'writer mut dyn Write, config: &'config Config, ) -> Renderer<'writer, 'config> { Renderer { writer, config } @@ -126,6 +141,7 @@ impl<'writer, 'config> Renderer<'writer, 'config> { &self.config.chars } + #[cfg(feature = "termcolor")] fn styles(&self) -> &'config Styles { &self.config.styles } @@ -157,6 +173,7 @@ impl<'writer, 'config> Renderer<'writer, 'config> { // ```text // error // ``` + #[cfg(feature = "termcolor")] self.set_color(self.styles().header(severity))?; match severity { Severity::Bug => write!(self, "bug")?, @@ -180,8 +197,10 @@ impl<'writer, 'config> Renderer<'writer, 'config> { // ```text // : unexpected type in `+` application // ``` + #[cfg(feature = "termcolor")] self.set_color(&self.styles().header_message)?; write!(self, ": {}", message)?; + #[cfg(feature = "termcolor")] self.reset()?; writeln!(self)?; @@ -207,12 +226,14 @@ impl<'writer, 'config> Renderer<'writer, 'config> { ) -> Result<(), Error> { self.outer_gutter(outer_padding)?; + #[cfg(feature = "termcolor")] self.set_color(&self.styles().source_border)?; write!(self, "{}", self.chars().snippet_start)?; + #[cfg(feature = "termcolor")] self.reset()?; write!(self, " ")?; - self.snippet_locus(&locus)?; + self.snippet_locus(locus)?; writeln!(self)?; @@ -225,6 +246,7 @@ impl<'writer, 'config> Renderer<'writer, 'config> { /// 10 │ │ muffin. Halvah croissant candy canes bonbon candy. Apple pie jelly /// │ ╭─│─────────^ /// ``` + #[allow(clippy::too_many_arguments)] pub fn render_snippet_source( &mut self, outer_padding: usize, @@ -291,9 +313,11 @@ impl<'writer, 'config> Renderer<'writer, 'config> { // Set the source color if we are in a primary label if is_primary && !in_primary { + #[cfg(feature = "termcolor")] self.set_color(self.styles().label(severity, LabelStyle::Primary))?; in_primary = true; } else if !is_primary && in_primary { + #[cfg(feature = "termcolor")] self.reset()?; in_primary = false; } @@ -304,6 +328,7 @@ impl<'writer, 'config> Renderer<'writer, 'config> { } } if in_primary { + #[cfg(feature = "termcolor")] self.reset()?; } writeln!(self)?; @@ -368,8 +393,8 @@ impl<'writer, 'config> Renderer<'writer, 'config> { if !message.is_empty() { num_messages += 1; } - max_label_start = std::cmp::max(max_label_start, range.start); - max_label_end = std::cmp::max(max_label_end, range.end); + max_label_start = core::cmp::max(max_label_start, range.start); + max_label_end = core::cmp::max(max_label_end, range.end); // This is a candidate for the trailing label, so let's record it. if range.end == max_label_end { if message.is_empty() { @@ -418,7 +443,7 @@ impl<'writer, 'config> Renderer<'writer, 'config> { // 1 │ Hello world! // │ ^ // ``` - .chain(std::iter::once((placeholder_metrics, '\0'))) + .chain(core::iter::once((placeholder_metrics, '\0'))) { // Find the current label style at this column let column_range = metrics.byte_index..(metrics.byte_index + ch.len_utf8()); @@ -431,8 +456,13 @@ impl<'writer, 'config> Renderer<'writer, 'config> { // Update writer style if necessary if previous_label_style != current_label_style { match current_label_style { - None => self.reset()?, + None => { + #[cfg(feature = "termcolor")] + self.reset()?; + } + #[cfg_attr(not(feature = "termcolor"), allow(unused))] Some(label_style) => { + #[cfg(feature = "termcolor")] self.set_color(self.styles().label(severity, label_style))?; } } @@ -454,13 +484,17 @@ impl<'writer, 'config> Renderer<'writer, 'config> { } // Reset style if it was previously set if previous_label_style.is_some() { + #[cfg(feature = "termcolor")] self.reset()?; } // Write first trailing label message + #[cfg_attr(not(feature = "termcolor"), allow(unused))] if let Some((_, (label_style, _, message))) = trailing_label { write!(self, " ")?; + #[cfg(feature = "termcolor")] self.set_color(self.styles().label(severity, *label_style))?; write!(self, "{}", message)?; + #[cfg(feature = "termcolor")] self.reset()?; } writeln!(self)?; @@ -499,6 +533,7 @@ impl<'writer, 'config> Renderer<'writer, 'config> { // │ first borrow later used by call // │ help: some help here // ``` + #[cfg_attr(not(feature = "termcolor"), allow(unused))] for (label_style, range, message) in hanging_labels(single_labels, trailing_label).rev() { @@ -515,8 +550,10 @@ impl<'writer, 'config> Renderer<'writer, 'config> { .char_indices() .take_while(|(byte_index, _)| *byte_index < range.start), )?; + #[cfg(feature = "termcolor")] self.set_color(self.styles().label(severity, *label_style))?; write!(self, "{}", message)?; + #[cfg(feature = "termcolor")] self.reset()?; writeln!(self)?; } @@ -646,8 +683,10 @@ impl<'writer, 'config> Renderer<'writer, 'config> { self.outer_gutter(outer_padding)?; match note_line_index { 0 => { + #[cfg(feature = "termcolor")] self.set_color(&self.styles().note_bullet)?; write!(self, "{}", self.chars().note_bullet)?; + #[cfg(feature = "termcolor")] self.reset()?; } _ => write!(self, " ")?, @@ -710,6 +749,7 @@ impl<'writer, 'config> Renderer<'writer, 'config> { line_number: usize, outer_padding: usize, ) -> Result<(), Error> { + #[cfg(feature = "termcolor")] self.set_color(&self.styles().line_number)?; write!( self, @@ -717,6 +757,7 @@ impl<'writer, 'config> Renderer<'writer, 'config> { line_number = line_number, width = outer_padding, )?; + #[cfg(feature = "termcolor")] self.reset()?; write!(self, " ")?; Ok(()) @@ -724,16 +765,20 @@ impl<'writer, 'config> Renderer<'writer, 'config> { /// The left-hand border of a source line. fn border_left(&mut self) -> Result<(), Error> { + #[cfg(feature = "termcolor")] self.set_color(&self.styles().source_border)?; write!(self, "{}", self.chars().source_border_left)?; + #[cfg(feature = "termcolor")] self.reset()?; Ok(()) } /// The broken left-hand border of a source line. fn border_left_break(&mut self) -> Result<(), Error> { + #[cfg(feature = "termcolor")] self.set_color(&self.styles().source_border)?; write!(self, "{}", self.chars().source_border_left_break)?; + #[cfg(feature = "termcolor")] self.reset()?; Ok(()) } @@ -741,7 +786,7 @@ impl<'writer, 'config> Renderer<'writer, 'config> { /// Write vertical lines pointing to carets. fn caret_pointers( &mut self, - severity: Severity, + #[cfg_attr(not(feature = "termcolor"), allow(unused))] severity: Severity, max_label_start: usize, single_labels: &[SingleLabel<'_>], trailing_label: Option<(usize, &SingleLabel<'_>)>, @@ -756,9 +801,12 @@ impl<'writer, 'config> Renderer<'writer, 'config> { let mut spaces = match label_style { None => 0..metrics.unicode_width, + #[cfg_attr(not(feature = "termcolor"), allow(unused))] Some(label_style) => { + #[cfg(feature = "termcolor")] self.set_color(self.styles().label(severity, label_style))?; write!(self, "{}", self.chars().pointer_left)?; + #[cfg(feature = "termcolor")] self.reset()?; 1..metrics.unicode_width } @@ -779,21 +827,26 @@ impl<'writer, 'config> Renderer<'writer, 'config> { /// ``` fn label_multi_left( &mut self, - severity: Severity, - label_style: LabelStyle, + #[cfg_attr(not(feature = "termcolor"), allow(unused))] severity: Severity, + #[cfg_attr(not(feature = "termcolor"), allow(unused))] label_style: LabelStyle, underline: Option, ) -> Result<(), Error> { match underline { None => write!(self, " ")?, // Continue an underline horizontally + #[cfg_attr(not(feature = "termcolor"), allow(unused))] Some(label_style) => { + #[cfg(feature = "termcolor")] self.set_color(self.styles().label(severity, label_style))?; write!(self, "{}", self.chars().multi_top)?; + #[cfg(feature = "termcolor")] self.reset()?; } } + #[cfg(feature = "termcolor")] self.set_color(self.styles().label(severity, label_style))?; write!(self, "{}", self.chars().multi_left)?; + #[cfg(feature = "termcolor")] self.reset()?; Ok(()) } @@ -805,12 +858,14 @@ impl<'writer, 'config> Renderer<'writer, 'config> { /// ``` fn label_multi_top_left( &mut self, - severity: Severity, - label_style: LabelStyle, + #[cfg_attr(not(feature = "termcolor"), allow(unused))] severity: Severity, + #[cfg_attr(not(feature = "termcolor"), allow(unused))] label_style: LabelStyle, ) -> Result<(), Error> { write!(self, " ")?; + #[cfg(feature = "termcolor")] self.set_color(self.styles().label(severity, label_style))?; write!(self, "{}", self.chars().multi_top_left)?; + #[cfg(feature = "termcolor")] self.reset()?; Ok(()) } @@ -822,12 +877,14 @@ impl<'writer, 'config> Renderer<'writer, 'config> { /// ``` fn label_multi_bottom_left( &mut self, - severity: Severity, - label_style: LabelStyle, + #[cfg_attr(not(feature = "termcolor"), allow(unused))] severity: Severity, + #[cfg_attr(not(feature = "termcolor"), allow(unused))] label_style: LabelStyle, ) -> Result<(), Error> { write!(self, " ")?; + #[cfg(feature = "termcolor")] self.set_color(self.styles().label(severity, label_style))?; write!(self, "{}", self.chars().multi_bottom_left)?; + #[cfg(feature = "termcolor")] self.reset()?; Ok(()) } @@ -839,11 +896,12 @@ impl<'writer, 'config> Renderer<'writer, 'config> { /// ``` fn label_multi_top_caret( &mut self, - severity: Severity, + #[cfg_attr(not(feature = "termcolor"), allow(unused))] severity: Severity, label_style: LabelStyle, source: &str, start: usize, ) -> Result<(), Error> { + #[cfg(feature = "termcolor")] self.set_color(self.styles().label(severity, label_style))?; for (metrics, _) in self @@ -860,6 +918,7 @@ impl<'writer, 'config> Renderer<'writer, 'config> { LabelStyle::Secondary => self.config.chars.multi_secondary_caret_start, }; write!(self, "{}", caret_start)?; + #[cfg(feature = "termcolor")] self.reset()?; writeln!(self)?; Ok(()) @@ -872,12 +931,13 @@ impl<'writer, 'config> Renderer<'writer, 'config> { /// ``` fn label_multi_bottom_caret( &mut self, - severity: Severity, + #[cfg_attr(not(feature = "termcolor"), allow(unused))] severity: Severity, label_style: LabelStyle, source: &str, start: usize, message: &str, ) -> Result<(), Error> { + #[cfg(feature = "termcolor")] self.set_color(self.styles().label(severity, label_style))?; for (metrics, _) in self @@ -897,6 +957,7 @@ impl<'writer, 'config> Renderer<'writer, 'config> { if !message.is_empty() { write!(self, " {}", message)?; } + #[cfg(feature = "termcolor")] self.reset()?; writeln!(self)?; Ok(()) @@ -905,18 +966,21 @@ impl<'writer, 'config> Renderer<'writer, 'config> { /// Writes an empty gutter space, or continues an underline horizontally. fn inner_gutter_column( &mut self, - severity: Severity, + #[cfg_attr(not(feature = "termcolor"), allow(unused))] severity: Severity, underline: Option, ) -> Result<(), Error> { match underline { None => self.inner_gutter_space(), + #[cfg_attr(not(feature = "termcolor"), allow(unused))] Some((label_style, vertical_bound)) => { + #[cfg(feature = "termcolor")] self.set_color(self.styles().label(severity, label_style))?; let ch = match vertical_bound { VerticalBound::Top => self.config.chars.multi_top, VerticalBound::Bottom => self.config.chars.multi_bottom, }; write!(self, "{0}{0}", ch)?; + #[cfg(feature = "termcolor")] self.reset()?; Ok(()) } @@ -957,7 +1021,23 @@ impl<'writer, 'config> Renderer<'writer, 'config> { } } -impl<'writer, 'config> Write for Renderer<'writer, 'config> { +#[cfg(not(feature = "std"))] +impl Write for Renderer<'_, '_> { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + self.writer.write_str(s) + } + + fn write_char(&mut self, c: char) -> core::fmt::Result { + self.writer.write_char(c) + } + + fn write_fmt(&mut self, args: Arguments<'_>) -> core::fmt::Result { + self.writer.write_fmt(args) + } +} + +#[cfg(feature = "std")] +impl Write for Renderer<'_, '_> { fn write(&mut self, buf: &[u8]) -> io::Result { self.writer.write(buf) } @@ -967,7 +1047,8 @@ impl<'writer, 'config> Write for Renderer<'writer, 'config> { } } -impl<'writer, 'config> WriteColor for Renderer<'writer, 'config> { +#[cfg(feature = "termcolor")] +impl WriteColor for Renderer<'_, '_> { fn supports_color(&self) -> bool { self.writer.supports_color() } @@ -992,8 +1073,8 @@ struct Metrics { /// Check if two ranges overlap fn is_overlapping(range0: &Range, range1: &Range) -> bool { - let start = std::cmp::max(range0.start, range1.start); - let end = std::cmp::min(range0.end, range1.end); + let start = core::cmp::max(range0.start, range1.start); + let end = core::cmp::min(range0.end, range1.end); start < end } diff --git a/third_party/rust/codespan-reporting/src/term/views.rs b/third_party/rust/codespan-reporting/src/term/views.rs index f09d9582a689..364cd65ce8d6 100644 --- a/third_party/rust/codespan-reporting/src/term/views.rs +++ b/third_party/rust/codespan-reporting/src/term/views.rs @@ -1,18 +1,18 @@ -use std::ops::Range; +use alloc::{ + string::{String, ToString}, + vec, + vec::Vec, +}; +use core::ops::Range; use crate::diagnostic::{Diagnostic, LabelStyle}; use crate::files::{Error, Files, Location}; use crate::term::renderer::{Locus, MultiLabel, Renderer, SingleLabel}; use crate::term::Config; -/// Count the number of decimal digits in `n`. -fn count_digits(mut n: usize) -> usize { - let mut count = 0; - while n != 0 { - count += 1; - n /= 10; // remove last digit - } - count +/// Calculate the number of decimal digits in `n`. +fn count_digits(n: usize) -> usize { + n.ilog10() as usize + 1 } /// Output a richly formatted diagnostic, with source code previews. @@ -34,13 +34,13 @@ where pub fn render<'files>( &self, - files: &'files impl Files<'files, FileId = FileId>, + files: &'files (impl Files<'files, FileId = FileId> + ?Sized), renderer: &mut Renderer<'_, '_>, ) -> Result<(), Error> where FileId: 'files, { - use std::collections::BTreeMap; + use alloc::collections::BTreeMap; struct LabeledFile<'diagnostic, FileId> { file_id: FileId, @@ -72,7 +72,7 @@ where struct Line<'diagnostic> { number: usize, - range: std::ops::Range, + range: core::ops::Range, // TODO: How do we reuse these allocations? single_labels: Vec>, multi_labels: Vec<(usize, LabelStyle, MultiLabel<'diagnostic>)>, @@ -94,8 +94,8 @@ where let end_line_number = files.line_number(label.file_id, end_line_index)?; let end_line_range = files.line_range(label.file_id, end_line_index)?; - outer_padding = std::cmp::max(outer_padding, count_digits(start_line_number)); - outer_padding = std::cmp::max(outer_padding, count_digits(end_line_number)); + outer_padding = core::cmp::max(outer_padding, count_digits(start_line_number)); + outer_padding = core::cmp::max(outer_padding, count_digits(end_line_number)); // NOTE: This could be made more efficient by using an associative // data structure like a hashmap or B-tree, but we use a vector to @@ -135,6 +135,43 @@ where } }; + // insert context lines before label + // start from 1 because 0 would be the start of the label itself + for offset in 1..self.config.before_label_lines + 1 { + let index = if let Some(index) = start_line_index.checked_sub(offset) { + index + } else { + // we are going from smallest to largest offset, so if + // the offset can not be subtracted from the start we + // reached the first line + break; + }; + + if let Ok(range) = files.line_range(label.file_id, index) { + let line = + labeled_file.get_or_insert_line(index, range, start_line_number - offset); + line.must_render = true; + } else { + break; + } + } + + // insert context lines after label + // start from 1 because 0 would be the end of the label itself + for offset in 1..self.config.after_label_lines + 1 { + let index = end_line_index + .checked_add(offset) + .expect("line index too big"); + + if let Ok(range) = files.line_range(label.file_id, index) { + let line = + labeled_file.get_or_insert_line(index, range, end_line_number + offset); + line.must_render = true; + } else { + break; + } + } + if start_line_index == end_line_index { // Single line // @@ -217,7 +254,7 @@ where let line_range = files.line_range(label.file_id, line_index)?; let line_number = files.line_number(label.file_id, line_index)?; - outer_padding = std::cmp::max(outer_padding, count_digits(line_number)); + outer_padding = core::cmp::max(outer_padding, count_digits(line_number)); let line = labeled_file.get_or_insert_line(line_index, line_range, line_number); @@ -324,7 +361,7 @@ where // Check to see if we need to render any intermediate stuff // before rendering the next line. - if let Some((next_line_index, _)) = lines.peek() { + if let Some((next_line_index, next_line)) = lines.peek() { match next_line_index.checked_sub(*line_index) { // Consecutive lines Some(1) => {} @@ -361,7 +398,7 @@ where outer_padding, self.diagnostic.severity, labeled_file.num_multi_labels, - &line.multi_labels, + &next_line.multi_labels, )?; } } @@ -420,7 +457,7 @@ where pub fn render<'files>( &self, - files: &'files impl Files<'files, FileId = FileId>, + files: &'files (impl Files<'files, FileId = FileId> + ?Sized), renderer: &mut Renderer<'_, '_>, ) -> Result<(), Error> where diff --git a/third_party/rust/codespan-reporting/tests/snapshots/term__surrounding_lines__rich_no_color.snap b/third_party/rust/codespan-reporting/tests/snapshots/term__surrounding_lines__rich_no_color.snap new file mode 100644 index 000000000000..96b1c702007a --- /dev/null +++ b/third_party/rust/codespan-reporting/tests/snapshots/term__surrounding_lines__rich_no_color.snap @@ -0,0 +1,32 @@ +--- +source: codespan-reporting/tests/term.rs +expression: TEST_DATA.emit_no_color(&config) + +--- +error: Unknown attribute macro + ┌─ surroundingLines.fun:1:3 + │ +1 │ #[foo] + │ ^^^ No attribute macro `foo` known +2 │ fn main() { + +error: Missing argument for format + ┌─ surroundingLines.fun:5:9 + │ +2 │ fn main() { +3 │ println!( +4 │ "{}", + │ -- Unable to use `{}`-directive to display `Foo` +5 │ Foo + │ ^^^ No instance of std::fmt::Display exists for type Foo +6 │ ); + +error: Syntax error + ┌─ surroundingLines.fun:9:11 + │ +7 │ } +8 │ +9 │ struct Foo + │ ^ Missing a semicolon + + diff --git a/third_party/rust/codespan-reporting/tests/support/color_buffer.rs b/third_party/rust/codespan-reporting/tests/support/color_buffer.rs index b51360ce6358..1d9241bfd292 100644 --- a/third_party/rust/codespan-reporting/tests/support/color_buffer.rs +++ b/third_party/rust/codespan-reporting/tests/support/color_buffer.rs @@ -92,7 +92,7 @@ impl WriteColor for ColorBuffer { } Ok(false) - }; + } if let Some(fg) = spec.fg() { first = write_first(first, self)?; diff --git a/third_party/rust/codespan-reporting/tests/support/mod.rs b/third_party/rust/codespan-reporting/tests/support/mod.rs index 4f7313ac3647..c674b31df56c 100644 --- a/third_party/rust/codespan-reporting/tests/support/mod.rs +++ b/third_party/rust/codespan-reporting/tests/support/mod.rs @@ -15,17 +15,17 @@ pub struct TestData<'files, F: Files<'files>> { impl<'files, F: Files<'files>> TestData<'files, F> { fn emit(&'files self, mut writer: W, config: &Config) -> W { for diagnostic in &self.diagnostics { - emit(&mut writer, config, &self.files, &diagnostic).unwrap(); + emit(&mut writer, config, &self.files, diagnostic).unwrap(); } writer } pub fn emit_color(&'files self, config: &Config) -> String { - self.emit(ColorBuffer::new(), &config).into_string() + self.emit(ColorBuffer::new(), config).into_string() } pub fn emit_no_color(&'files self, config: &Config) -> String { - let buffer = self.emit(Buffer::no_color(), &config); + let buffer = self.emit(Buffer::no_color(), config); String::from_utf8_lossy(buffer.as_slice()).into_owned() } } diff --git a/third_party/rust/codespan-reporting/tests/term.rs b/third_party/rust/codespan-reporting/tests/term.rs index 035db9b459ea..cff58a509b33 100644 --- a/third_party/rust/codespan-reporting/tests/term.rs +++ b/third_party/rust/codespan-reporting/tests/term.rs @@ -893,11 +893,11 @@ mod unicode_spans { lazy_static::lazy_static! { static ref TEST_DATA: TestData<'static, SimpleFile<&'static str, String>> = { - let moon_phases = format!("{}", r#"🐄🌑🐄🌒🐄🌓🐄🌔🐄🌕🐄🌖🐄🌗🐄🌘🐄"#); + let moon_phases = r#"🐄🌑🐄🌒🐄🌓🐄🌔🐄🌕🐄🌖🐄🌗🐄🌘🐄"#.to_string(); let invalid_start = 1; let invalid_end = "🐄".len() - 1; - assert_eq!(moon_phases.is_char_boundary(invalid_start), false); - assert_eq!(moon_phases.is_char_boundary(invalid_end), false); + assert!(!moon_phases.is_char_boundary(invalid_start)); + assert!(!moon_phases.is_char_boundary(invalid_end)); assert_eq!("🐄".len(), 4); let file = SimpleFile::new( "moon_jump.rs", @@ -1051,3 +1051,58 @@ mod multiline_omit { test_emit!(rich_no_color); } + +mod surrounding_lines { + use super::*; + + lazy_static::lazy_static! { + static ref TEST_CONFIG: Config = Config { + styles: Styles::with_blue(Color::Blue), + before_label_lines: 2, + after_label_lines: 1, + ..Config::default() + }; + static ref TEST_DATA: TestData<'static, SimpleFiles<&'static str, String>> = { + let mut files = SimpleFiles::new(); + + let file_id = files.add( + "surroundingLines.fun", + unindent::unindent( + r#" + #[foo] + fn main() { + println!( + "{}", + Foo + ); + } + + struct Foo"#, + ), + ); + + let diagnostics = vec![ + Diagnostic::error() + .with_message("Unknown attribute macro") + .with_labels(vec![ + Label::primary(file_id, 2..5).with_message("No attribute macro `foo` known"), + ]), + Diagnostic::error() + .with_message("Missing argument for format") + .with_labels(vec![ + Label::primary(file_id, 55..58).with_message("No instance of std::fmt::Display exists for type Foo"), + Label::secondary(file_id, 42..44).with_message("Unable to use `{}`-directive to display `Foo`"), + ]), + Diagnostic::error() + .with_message("Syntax error") + .with_labels(vec![ + Label::primary(file_id, 79..79).with_message("Missing a semicolon"), + ]), + ]; + + TestData { files, diagnostics } + }; + } + + test_emit!(rich_no_color); +} diff --git a/third_party/rust/naga/.cargo-checksum.json b/third_party/rust/naga/.cargo-checksum.json index 4140f63b612f..d4b1d1f82986 100644 --- a/third_party/rust/naga/.cargo-checksum.json +++ b/third_party/rust/naga/.cargo-checksum.json @@ -1 +1 @@ -{"files":{".cargo/config.toml":"d7389d2a0c08ec72b79e83a3c76980903e3f9123625c32e69c798721193e2e74","CHANGELOG.md":"e60105d413f857e37dae165f819c47491d0a595183d3c9146b259d811b98b14f","Cargo.toml":"4172d535a12ec21d85e0b41a0977a106ecb54b76cb062f5bdbc5f4d19e29da89","README.md":"9550cbc1a518ad0f624aabe12c342c72f670705cb4a6878c0c87d172f1dacea0","build.rs":"a08840f5b18ab09f453e1fc3a09dd16df83caa7798b6ecb7e7ac2f8addab276b","src/arena/handle.rs":"897b2b0eebe0d9ae6a65bf2e8c210c8391924da06ef4c9e2a1225ad622400b6c","src/arena/handle_set.rs":"271f89abb20927eb9f45f552ad2365c4976885cb3dc50078950795f8741b402e","src/arena/handlevec.rs":"999de9d55d01213789072a63ad4ae9b4635e6653984d38db8b308d42bb1e7be3","src/arena/mod.rs":"da53a1c73159240d980ecc3a7be4a2b33b864ff387c5d772c72d7be131a2b671","src/arena/range.rs":"b783969dfe32b4937593d871aa5190d561bdd79b6f615da53cb54346e300b9e2","src/arena/unique_arena.rs":"031bba0907a0eccf7d652a22f88d0e27289072ac72b123743a9602d6cf68b587","src/back/continue_forward.rs":"8194d238763caa6d5601ec3af56ba39a471c39945f43152b58d582092c99aefa","src/back/dot/mod.rs":"345a6ef4fa0e9f19ec1c87d436f4c0df13f5e3094e45f470cf8ceb5bc4641c58","src/back/glsl/features.rs":"1849818acef514642430cccef0cdbe79700a3da7de617cb001956f25b71a6330","src/back/glsl/keywords.rs":"0f004f184c4db192b4f362f92d0d6afae2a70af795949ba20c24cc2a2029ff12","src/back/glsl/mod.rs":"42da96f74684f955cf6659f932f27ef94d403972bc96ac6021930c3514e9a61e","src/back/hlsl/conv.rs":"d238a55a66abd5d006138022d1db053d03fb5b93db6a751c4c123fb9ba07cb34","src/back/hlsl/help.rs":"0b70b66ea98c6804a63f99115cf802dc741ef50a29ff92d5d00473fac998a587","src/back/hlsl/keywords.rs":"984fb9a19afd8e267a2c839e8ddeae65ad20261534f35f33ab4124af7f9b5865","src/back/hlsl/mod.rs":"f9bd94e821fde2ae77921bec8d8d344783ad3609c4e60d929c7a67ce585be137","src/back/hlsl/ray.rs":"259db3bc8fd5b8ec343fb8620c7cef50048cbea2b9db1f17dc8813ff848269df","src/back/hlsl/storage.rs":"7443cea2abb6ce8d0c0c9bfe805b37b5600bd28618de44ba1ebd8e4e38eac92f","src/back/hlsl/writer.rs":"038c6d639c60422fee4b1addcb296625015139f59d430e722ed0de83ace9f375","src/back/mod.rs":"dd798b019a33ef639ede0c92a126af691c9fef492e7b1107d358fb7b2a78fd2c","src/back/msl/keywords.rs":"5e923fcab06323dfd48748ff15cf36a5ccc155378f16c324ea186decc4300dff","src/back/msl/mod.rs":"4b4e50c8d4e8c24bcff81008a02b19d5dac5db9e3899cf9219e26f3554c60f36","src/back/msl/sampler.rs":"9be8805063258b0ff6b6db2e6856d59326caa18f6f64eaf413c5abfcbd1ad341","src/back/msl/writer.rs":"54ca8ac162d8b47f4383414c0e2353273c9ae03468587d8678a9751e6c311cde","src/back/pipeline_constants.rs":"9e51ed3812378835ca34a81f4c6f9cd070e80588c0b4ab69322bfe7c968d63da","src/back/spv/block.rs":"3302619b673e6a4e26f20324d879491d6d1b6cf791169f064c2a912f1cfebc37","src/back/spv/helpers.rs":"37ef7388b1e97edb2682b93f1f0eceb71039ecd54923d91346d974b29322077c","src/back/spv/image.rs":"c8891267d553e28ddefa0111959f6c04b80e1cade41527645e98832238128418","src/back/spv/index.rs":"5f0561a83011bcf482575d209c549ce08fce85b9dd95f179f6e8c2b112e0c8e5","src/back/spv/instructions.rs":"ec6dff2dcaa80efc3de92aa43aadc147be5ea52b31c10d28cf91d8079decab63","src/back/spv/layout.rs":"52b965e9efd572dac72e0ee51041256fcc666eb4b2eadebe786e8ff39f6e031a","src/back/spv/mod.rs":"19947a9758ab9e03260d158a3560b67b2084f013fa40e44e9e2b48ab8cef0e6e","src/back/spv/ray.rs":"0be4b977ac5f2508e5827fa0ce64dce3ef4a46959a7d5343fa391a8460755c07","src/back/spv/recyclable.rs":"8ea397d4d8d3f2cd5fbc8e0be94d136c2d6e0f0e8a4b5eb567dcc1be104c9ae5","src/back/spv/selection.rs":"aea4bb4da7c0fa4e907b8f1e185433a48f2f0eb7ded97fdd3225beb3f6c1f249","src/back/spv/subgroup.rs":"9003f0f653fd466f2568341e18583d49f639a3f4e6f30dbeb069c853f846ef33","src/back/spv/writer.rs":"6035c47852d9d89caaa0d821402e998c9746b4f516aed7fe17e1cedfd1018e6d","src/back/wgsl/mod.rs":"1b04d66e8dba609513d43431d1f0ee9a209fbfd8453862d6e8a7aa41f8910997","src/back/wgsl/polyfill/inverse/inverse_2x2_f16.wgsl":"9e7635d04724822931c805a8b35e76d6d294d447e4ea8d57b308ce45609bf736","src/back/wgsl/polyfill/inverse/inverse_2x2_f32.wgsl":"340d491abde07f93996391796db65a5f88402663eaf6b9d2d894d11cb8cf8b6d","src/back/wgsl/polyfill/inverse/inverse_3x3_f16.wgsl":"4f13a1a4b3e1b51f0f992d13c55cf854a80917554a4d13c997819fa1fe776ba4","src/back/wgsl/polyfill/inverse/inverse_3x3_f32.wgsl":"9b16d2f4b9e433c8e03a0cb46ab48508f3bf7e185ce1b4e26106c47e81a677cb","src/back/wgsl/polyfill/inverse/inverse_4x4_f16.wgsl":"86d39d1db5d03995b404950279db7f1698ad9622982aa319fdedb7532673235b","src/back/wgsl/polyfill/inverse/inverse_4x4_f32.wgsl":"dc510525ac2dce66389a8c4bf8b2f31f0dedd9e6debdbe4ffd939a0a7fc533d3","src/back/wgsl/polyfill/mod.rs":"f4ab3c9b9cdc36d16dab00d0f7f07d6e6beda0e27a36053e9b5ffeeb7ca18edc","src/back/wgsl/writer.rs":"6d1c6ab4f4527cc617c593ff32d431536ff6b277724f01ddc613fa6d2f5e3496","src/common/mod.rs":"dd29d3abd51cd1bdd84f35b39e665d6d3f87d98627f14a361e8e72db41152aeb","src/common/wgsl/diagnostics.rs":"4fec985b4c5cc6dfae4dd78bd7c850adc88a1761d7b6691de0355ea49300e532","src/common/wgsl/mod.rs":"d944915ff692c96aecca67737bccc2d5d9eb68f475166a2744f29a025f4a4c93","src/common/wgsl/to_wgsl.rs":"b2f10bebfe120b63ff1564100e5b0296b548ee93b980c96d2e06a51ba370574f","src/common/wgsl/types.rs":"1b69663990da6095d620552f8bdb4e3fe26e0ddf6dec44c1e06daf3652afdd3d","src/compact/expressions.rs":"ecd5e2ff40198e2b7e24f2ca19abfa16167ec58e9be320c9b1dc3268a57d147f","src/compact/functions.rs":"9618539e9d3641d85c11573447d9773e7376cad23b1fd868ed5565b5eb93e05f","src/compact/handle_set_map.rs":"c190919a19f1f6b8d064459114937fdd1eaeb057963e4eab8f05a5219759cf34","src/compact/mod.rs":"834a6660504d9c3c1dab2433a91701440bb8141cca6974fb78dbe61d15ba6418","src/compact/statements.rs":"9d877ff561e2d5d45fef9a8f5e0147ad55bcbaca73898949b689b41e683fdb60","src/compact/types.rs":"a955ce5e336afa8d26f750c14d4a6638dcee6b0b5e0fcd7c446d8f88a35d8277","src/diagnostic_filter.rs":"7478de8ed51cca0ef57e29c8a0f981f2876f8ae6805bd244fc3846efea9cb3cc","src/error.rs":"3157d57126886c9db0d3c5871a569eaf0b79d8f27292808300fe4f3e50732b1d","src/front/atomic_upgrade.rs":"86ce9f9628d92a1a09802cb534bb4310236b83f2799c921b81c687f009c589be","src/front/glsl/ast.rs":"15a4f7c56aa44529373c7aa2a266d1582b7775833de6adc6b8f5bfd54d85a669","src/front/glsl/builtins.rs":"f38de43ada63181e5cf059fc3cfe52c302295b0eaad075b4aee1dfef6ef366b6","src/front/glsl/context.rs":"11a7c1013d223db3ce5caff29ce538731b0a04f1fc27a8e85d850c778de1de82","src/front/glsl/error.rs":"aac48e6c69a974c3523a7085fc315fe8899cf17e3b5c65c2bddbfa4342985a9d","src/front/glsl/functions.rs":"31dd4749c3bafb29a46f387481d21b2b6d81e6c831f6dc1df770c89fb74549cd","src/front/glsl/lex.rs":"24706628b600b5ce435cef464c84196ac5d58013122a97e7b59d509cc25f85a2","src/front/glsl/mod.rs":"f4f1cce6911935b305c415afe3c15f84c7824a3bb828a5d15e6f9ae4b0316df0","src/front/glsl/offset.rs":"66bd524a2d17dc44f431430dcbbb74a771fdab43c9581e88bb1123e6cfec516b","src/front/glsl/parser.rs":"6a13b4737f53b09d5bbc0add01f8fc1b2633b7957f0318374edfe0b903939912","src/front/glsl/parser/declarations.rs":"9949649fba43636d03eaf7f7560d3bb3743b19c7204fb95859283ee84b5dd239","src/front/glsl/parser/expressions.rs":"e056fbdde3bc7c8473acbd485aecd14120d3dbefbabd813ddbc5cfedaf605889","src/front/glsl/parser/functions.rs":"302e24e06190aff555131c33f9a80b15df6a0390d6c776f888a44d5ef7df697e","src/front/glsl/parser/types.rs":"ee242048a65cd3709e16b70a3882e9296e615327480f2ad779e3d2523778181f","src/front/glsl/parser_tests.rs":"6834f0d595f4077266054e5da43e4f1b60e5c6780611ab0f530d9964cc62fad3","src/front/glsl/token.rs":"83780c0c1954ef216896c9d8a48e412b357783e00ccd4909a7a249935c742629","src/front/glsl/types.rs":"286395d82707a09d28b4c1a8bade917822478e53d8eb277ceec5fa9e71649ba2","src/front/glsl/variables.rs":"75d3e203a07befd011f5693ab8f2789e4f06821badb4974043cc4ee10bd5c6c9","src/front/interpolator.rs":"4d6c6639c01fba78ffb8d0af298094cc2d6bb33f8054dad4379fd9fe3af5a4c8","src/front/mod.rs":"e78e91bf1f17409c5e25fd360415e999bb69b159750d2c1673a3db9973f586e0","src/front/spv/convert.rs":"16b8281fc1ae75dc62a02893db2c5b6d8994166e78b3b6b8cac7a01e0da4eae2","src/front/spv/error.rs":"8e09837ab8f03898cbdacda52ff66830bb948a9684b3264a7e9546119c5adb42","src/front/spv/function.rs":"6da0d7dec521039a3c81669ff171c7f15673bb3e93402f2a0f1c8e6f92d7f004","src/front/spv/image.rs":"3631c8533e69673e1de3f7896be004f154b3151ae1cf7aa2ca105df689d24e43","src/front/spv/mod.rs":"db72d9af2cbcd08f0fa9ee97031f94c35893caa41174bf8f8bb18050bd48941d","src/front/spv/null.rs":"ee20287365e025e8bcc91f29df930ff8b63cb6d7f26db0b1789d54de614a7353","src/front/type_gen.rs":"4dfaeefc671b8b5a882d3dcbb1b32895eaa5b05ecd9380f30e652164b0aac58c","src/front/wgsl/error.rs":"5b781a5225cc447bbb6dc71ec801ced9d1bb87c8338904d36ae2802cd8dff98e","src/front/wgsl/index.rs":"1db1bee7074a3fe9668d2c2ba0bd5053d6443f4ea19a56e4cccf2aa2bc8a33c9","src/front/wgsl/lower/construction.rs":"e9797e34f6e8be3e32579bc162bb1d3f4a2fbf7450d9a54943c16a21b6f62a46","src/front/wgsl/lower/conversion.rs":"fa17798087afb61a2791b946c99854ad55580020969dc0820be9e14f10138de1","src/front/wgsl/lower/mod.rs":"6a6cccc57c640528746d815e479d549d02108ae6a9d8a09bc1751c45b5fcf5cf","src/front/wgsl/mod.rs":"435d381a2224682d60bc40f716a12ab00f239355e400a0d0cce40c055377cd73","src/front/wgsl/parse/ast.rs":"19c7ebafbecd29a795ea11e0e0f3c1a64d406594037ece7c3dce3670d808897a","src/front/wgsl/parse/conv.rs":"12a2d7199b09d0c95c7d5125b4987d7e54fc5811acb15d50978ab5c04beab916","src/front/wgsl/parse/directive.rs":"c96f33cef2c1d8a374fe1b3827538f7db33d6b7811a6e0914d29de80b8963257","src/front/wgsl/parse/directive/enable_extension.rs":"b076d8d90db1a374b4a49afbac522287f360c2d65e43db9103863e0a1c909557","src/front/wgsl/parse/directive/language_extension.rs":"b769f340f4f89410734d0d0fa3bc8530eb10ddb05bbd175da7bf61b062334cc0","src/front/wgsl/parse/lexer.rs":"a43060f637965045aabe3d6498ecf4b0bbc40aee4bb0156ca542b09b3c08178f","src/front/wgsl/parse/mod.rs":"4d8341d6db73f69e67dfc79f99f884beb5f573510522e7e8305b0de771f9d9fc","src/front/wgsl/parse/number.rs":"7af92c71031e4c4258e9d8d323f7ee99a2fd4be3b6975ab9b8b53b95431845d9","src/front/wgsl/tests.rs":"a50bd5d6d218fc0be8876d1f9db7936ff593d93805c5e85754ae6bf277beae32","src/ir/block.rs":"1c59138385fe197a4e96574c792010bcd216cfb0edb3ee4f3ad546dabb803778","src/ir/mod.rs":"7291de864bd269ac8406cc4433a5eb975254e7dc3b823804ff122617d5dd60da","src/keywords/mod.rs":"47a6fde012bf7d1e70f0fac7762f6a8e7dca6b9bbb99e2cada773c61527cfbfe","src/keywords/wgsl.rs":"60e8b7082d9df8c89544ad08ef7fdd8f7b75c48906cffb285fe4f53de04bb263","src/lib.rs":"c62861b9e3133d560034cdb7af9dea448c4c0d0da7b6ee2f1b69050571302c12","src/non_max_u32.rs":"01c87cc4244da9585e7ea404d3916aaa0005ae5d9d16ca9f33f33ef525c7a7df","src/proc/constant_evaluator.rs":"166c06adf5f59b076b9ecd5205210eeac6df7d9c12d6423650ef94745b24c666","src/proc/emitter.rs":"39ac886c651e2ad33c06a676a7e4826a0e93de0af660c01e8e4b1f7406742f88","src/proc/index.rs":"9a9db3e813b92d0751f9e6edfc8147a3c5bfd30ae76e7e97e8d083f94ab48f07","src/proc/layouter.rs":"bf50fed8cf4b15400218fa1cf897f04925795939b09c9ce658eb8bc7559a5705","src/proc/mod.rs":"334b2c45e53281dcca14fbb550991dac45d4a44ed2b056ad176fa07d2f94ccd1","src/proc/namer.rs":"70ad6d5fa77b3d7713e738ab1625d9c8a6f243e89239c4c8772749321a0f2c56","src/proc/terminator.rs":"66b0082dce3b29d6d20e9a891c66524d756c8e567ac844d61355248ac4623b07","src/proc/type_methods.rs":"c6a71c8ed4e3c68263a77a06207e82b82743cd592b17c2d9b216ce33e9e79450","src/proc/typifier.rs":"2e54c6fbe1b9afdd4875555d04905460ab6932d712c1a977c4cb4c5c7a2f43cb","src/span.rs":"e4ebe5d7bc6b03c158881ae0bf90ed398777fd904fb9c1f9774821fbd43fa1f1","src/valid/analyzer.rs":"9c662636da86bd9c0d3f101b8023b184a6a4d23b9dabd6c2fda33fd1ea82e835","src/valid/compose.rs":"f27a6bc13180fcd1ecbbab698faab5e25ecf0115676fda6f367fb3e8fb72a6b0","src/valid/expression.rs":"c469c20c3fadca222370d0acf6f54b8080e5e3f5fe25fa27c2edf368b9bc7a74","src/valid/function.rs":"d6aec942d3ff2c293506e0ae6db759df7a938bbfcfa2eb5b15ad68794a6df971","src/valid/handles.rs":"2750068c629fcad7ed876c1fd4b117d2deba5d3c9e9d6ed686df2c1a79df1fa1","src/valid/interface.rs":"836716fb8bc86ea5f82dc93d2db85409b5490c593a3b8e88312fc513c277aa33","src/valid/mod.rs":"9a778054f732880fd0468e59c61d33e4b6060f287b951647228a4a53c1444a20","src/valid/type.rs":"0947af787fcbd40ac74dc6fb35c1973879cbd548d1cb815eb0907c0a7bf7c362"},"package":null} \ No newline at end of file +{"files":{".cargo/config.toml":"d7389d2a0c08ec72b79e83a3c76980903e3f9123625c32e69c798721193e2e74","CHANGELOG.md":"e60105d413f857e37dae165f819c47491d0a595183d3c9146b259d811b98b14f","Cargo.toml":"487b4a8d12d4743b047ee508672935ba104ef71dbae8b1dcbe81680832fc9522","README.md":"9550cbc1a518ad0f624aabe12c342c72f670705cb4a6878c0c87d172f1dacea0","build.rs":"a08840f5b18ab09f453e1fc3a09dd16df83caa7798b6ecb7e7ac2f8addab276b","src/arena/handle.rs":"897b2b0eebe0d9ae6a65bf2e8c210c8391924da06ef4c9e2a1225ad622400b6c","src/arena/handle_set.rs":"271f89abb20927eb9f45f552ad2365c4976885cb3dc50078950795f8741b402e","src/arena/handlevec.rs":"999de9d55d01213789072a63ad4ae9b4635e6653984d38db8b308d42bb1e7be3","src/arena/mod.rs":"3f7090caa85b04a365514eb345615dffb44c5edf32429f519882fd8b724de7b0","src/arena/range.rs":"b783969dfe32b4937593d871aa5190d561bdd79b6f615da53cb54346e300b9e2","src/arena/unique_arena.rs":"46530ea72e1758080752ab79e3645bdd8af784174f88efd730e50cdf9a903fe4","src/back/continue_forward.rs":"8194d238763caa6d5601ec3af56ba39a471c39945f43152b58d582092c99aefa","src/back/dot/mod.rs":"345a6ef4fa0e9f19ec1c87d436f4c0df13f5e3094e45f470cf8ceb5bc4641c58","src/back/glsl/features.rs":"1849818acef514642430cccef0cdbe79700a3da7de617cb001956f25b71a6330","src/back/glsl/keywords.rs":"0f004f184c4db192b4f362f92d0d6afae2a70af795949ba20c24cc2a2029ff12","src/back/glsl/mod.rs":"a556abf45d2ce8713d73d051bbf3cfec025000963b66be5ffe35d3b7224b1cbf","src/back/hlsl/conv.rs":"d238a55a66abd5d006138022d1db053d03fb5b93db6a751c4c123fb9ba07cb34","src/back/hlsl/help.rs":"05c0c739e9b7c92288e6724a742821481cbcb93200642a0812809c18f3825793","src/back/hlsl/keywords.rs":"e1cfffb593bc1909797fc45ccd25e73094aa1a04a1781ccf7f0d47569241db88","src/back/hlsl/mod.rs":"069b43df569b34c2819c1add4d25951e662f9d1958f5e1ffa7f75c3e67bcf3be","src/back/hlsl/ray.rs":"259db3bc8fd5b8ec343fb8620c7cef50048cbea2b9db1f17dc8813ff848269df","src/back/hlsl/storage.rs":"7443cea2abb6ce8d0c0c9bfe805b37b5600bd28618de44ba1ebd8e4e38eac92f","src/back/hlsl/writer.rs":"92902e9d44cb87fc486833e4246a1aff8535421b30260f8564d5de8f554a9cee","src/back/mod.rs":"dd798b019a33ef639ede0c92a126af691c9fef492e7b1107d358fb7b2a78fd2c","src/back/msl/keywords.rs":"3f0f2ff1859d29d54457c3604caffbf5f0ea678dbb7d68e0f376c2eef0634302","src/back/msl/mod.rs":"4b4e50c8d4e8c24bcff81008a02b19d5dac5db9e3899cf9219e26f3554c60f36","src/back/msl/sampler.rs":"9be8805063258b0ff6b6db2e6856d59326caa18f6f64eaf413c5abfcbd1ad341","src/back/msl/writer.rs":"6909ff9de3595583b5d7ef0ad41b0c045a903273346175d44628906375d792f3","src/back/pipeline_constants.rs":"9e51ed3812378835ca34a81f4c6f9cd070e80588c0b4ab69322bfe7c968d63da","src/back/spv/block.rs":"825afefffb6b55e2ccbed387b4bf2bac04f8c264dde2d2558800fab0f3ccfc1b","src/back/spv/helpers.rs":"37ef7388b1e97edb2682b93f1f0eceb71039ecd54923d91346d974b29322077c","src/back/spv/image.rs":"c8891267d553e28ddefa0111959f6c04b80e1cade41527645e98832238128418","src/back/spv/index.rs":"5f0561a83011bcf482575d209c549ce08fce85b9dd95f179f6e8c2b112e0c8e5","src/back/spv/instructions.rs":"ec6dff2dcaa80efc3de92aa43aadc147be5ea52b31c10d28cf91d8079decab63","src/back/spv/layout.rs":"52b965e9efd572dac72e0ee51041256fcc666eb4b2eadebe786e8ff39f6e031a","src/back/spv/mod.rs":"19947a9758ab9e03260d158a3560b67b2084f013fa40e44e9e2b48ab8cef0e6e","src/back/spv/ray.rs":"0be4b977ac5f2508e5827fa0ce64dce3ef4a46959a7d5343fa391a8460755c07","src/back/spv/recyclable.rs":"8ea397d4d8d3f2cd5fbc8e0be94d136c2d6e0f0e8a4b5eb567dcc1be104c9ae5","src/back/spv/selection.rs":"aea4bb4da7c0fa4e907b8f1e185433a48f2f0eb7ded97fdd3225beb3f6c1f249","src/back/spv/subgroup.rs":"9003f0f653fd466f2568341e18583d49f639a3f4e6f30dbeb069c853f846ef33","src/back/spv/writer.rs":"6035c47852d9d89caaa0d821402e998c9746b4f516aed7fe17e1cedfd1018e6d","src/back/wgsl/mod.rs":"1b04d66e8dba609513d43431d1f0ee9a209fbfd8453862d6e8a7aa41f8910997","src/back/wgsl/polyfill/inverse/inverse_2x2_f16.wgsl":"9e7635d04724822931c805a8b35e76d6d294d447e4ea8d57b308ce45609bf736","src/back/wgsl/polyfill/inverse/inverse_2x2_f32.wgsl":"340d491abde07f93996391796db65a5f88402663eaf6b9d2d894d11cb8cf8b6d","src/back/wgsl/polyfill/inverse/inverse_3x3_f16.wgsl":"4f13a1a4b3e1b51f0f992d13c55cf854a80917554a4d13c997819fa1fe776ba4","src/back/wgsl/polyfill/inverse/inverse_3x3_f32.wgsl":"9b16d2f4b9e433c8e03a0cb46ab48508f3bf7e185ce1b4e26106c47e81a677cb","src/back/wgsl/polyfill/inverse/inverse_4x4_f16.wgsl":"86d39d1db5d03995b404950279db7f1698ad9622982aa319fdedb7532673235b","src/back/wgsl/polyfill/inverse/inverse_4x4_f32.wgsl":"dc510525ac2dce66389a8c4bf8b2f31f0dedd9e6debdbe4ffd939a0a7fc533d3","src/back/wgsl/polyfill/mod.rs":"f4ab3c9b9cdc36d16dab00d0f7f07d6e6beda0e27a36053e9b5ffeeb7ca18edc","src/back/wgsl/writer.rs":"593c02c5fd82404eaea810a8d29f203835c703eb584e5c56956eed8952227577","src/common/mod.rs":"dd29d3abd51cd1bdd84f35b39e665d6d3f87d98627f14a361e8e72db41152aeb","src/common/wgsl/diagnostics.rs":"4fec985b4c5cc6dfae4dd78bd7c850adc88a1761d7b6691de0355ea49300e532","src/common/wgsl/mod.rs":"d944915ff692c96aecca67737bccc2d5d9eb68f475166a2744f29a025f4a4c93","src/common/wgsl/to_wgsl.rs":"91435023c5ef32632598f0f5b1dc83339d392856ba88051b1c523d00606f7c06","src/common/wgsl/types.rs":"1b69663990da6095d620552f8bdb4e3fe26e0ddf6dec44c1e06daf3652afdd3d","src/compact/expressions.rs":"ecd5e2ff40198e2b7e24f2ca19abfa16167ec58e9be320c9b1dc3268a57d147f","src/compact/functions.rs":"9618539e9d3641d85c11573447d9773e7376cad23b1fd868ed5565b5eb93e05f","src/compact/handle_set_map.rs":"c190919a19f1f6b8d064459114937fdd1eaeb057963e4eab8f05a5219759cf34","src/compact/mod.rs":"834a6660504d9c3c1dab2433a91701440bb8141cca6974fb78dbe61d15ba6418","src/compact/statements.rs":"9d877ff561e2d5d45fef9a8f5e0147ad55bcbaca73898949b689b41e683fdb60","src/compact/types.rs":"a955ce5e336afa8d26f750c14d4a6638dcee6b0b5e0fcd7c446d8f88a35d8277","src/diagnostic_filter.rs":"7478de8ed51cca0ef57e29c8a0f981f2876f8ae6805bd244fc3846efea9cb3cc","src/error.rs":"2ecf733ab81d7af168f490610bc3c1eeccadd550ff3aee313cf98f8a78d94dd8","src/front/atomic_upgrade.rs":"86ce9f9628d92a1a09802cb534bb4310236b83f2799c921b81c687f009c589be","src/front/glsl/ast.rs":"15a4f7c56aa44529373c7aa2a266d1582b7775833de6adc6b8f5bfd54d85a669","src/front/glsl/builtins.rs":"f38de43ada63181e5cf059fc3cfe52c302295b0eaad075b4aee1dfef6ef366b6","src/front/glsl/context.rs":"14fd933c79f49f4fb4ccabdc24ad31745777806f7464f111a4f7782662612a4b","src/front/glsl/error.rs":"74ab1a8e599d0e588d26e9066d2dbdea85bde0f229c42b8b786f14abc032794d","src/front/glsl/functions.rs":"1e8329c6de847336f5b196258d82f807f54021b6221da4d94326d071aeda25ba","src/front/glsl/lex.rs":"24706628b600b5ce435cef464c84196ac5d58013122a97e7b59d509cc25f85a2","src/front/glsl/mod.rs":"f4f1cce6911935b305c415afe3c15f84c7824a3bb828a5d15e6f9ae4b0316df0","src/front/glsl/offset.rs":"66bd524a2d17dc44f431430dcbbb74a771fdab43c9581e88bb1123e6cfec516b","src/front/glsl/parser.rs":"6a13b4737f53b09d5bbc0add01f8fc1b2633b7957f0318374edfe0b903939912","src/front/glsl/parser/declarations.rs":"9949649fba43636d03eaf7f7560d3bb3743b19c7204fb95859283ee84b5dd239","src/front/glsl/parser/expressions.rs":"e056fbdde3bc7c8473acbd485aecd14120d3dbefbabd813ddbc5cfedaf605889","src/front/glsl/parser/functions.rs":"302e24e06190aff555131c33f9a80b15df6a0390d6c776f888a44d5ef7df697e","src/front/glsl/parser/types.rs":"ee242048a65cd3709e16b70a3882e9296e615327480f2ad779e3d2523778181f","src/front/glsl/parser_tests.rs":"6834f0d595f4077266054e5da43e4f1b60e5c6780611ab0f530d9964cc62fad3","src/front/glsl/token.rs":"83780c0c1954ef216896c9d8a48e412b357783e00ccd4909a7a249935c742629","src/front/glsl/types.rs":"286395d82707a09d28b4c1a8bade917822478e53d8eb277ceec5fa9e71649ba2","src/front/glsl/variables.rs":"75d3e203a07befd011f5693ab8f2789e4f06821badb4974043cc4ee10bd5c6c9","src/front/interpolator.rs":"4d6c6639c01fba78ffb8d0af298094cc2d6bb33f8054dad4379fd9fe3af5a4c8","src/front/mod.rs":"e78e91bf1f17409c5e25fd360415e999bb69b159750d2c1673a3db9973f586e0","src/front/spv/convert.rs":"16b8281fc1ae75dc62a02893db2c5b6d8994166e78b3b6b8cac7a01e0da4eae2","src/front/spv/error.rs":"912b7d0348d4eaaddc0049ab789ed14874b1a7d77505db092dccc132e602ee84","src/front/spv/function.rs":"6da0d7dec521039a3c81669ff171c7f15673bb3e93402f2a0f1c8e6f92d7f004","src/front/spv/image.rs":"3631c8533e69673e1de3f7896be004f154b3151ae1cf7aa2ca105df689d24e43","src/front/spv/mod.rs":"767040d6b28319b73c49cf9d1314f937c96c1888da9d81e47be270483757905a","src/front/spv/null.rs":"ee20287365e025e8bcc91f29df930ff8b63cb6d7f26db0b1789d54de614a7353","src/front/type_gen.rs":"4dfaeefc671b8b5a882d3dcbb1b32895eaa5b05ecd9380f30e652164b0aac58c","src/front/wgsl/error.rs":"2fcebd59620254e5d4be71696721f97acab5b176e317a864aee224978042dcee","src/front/wgsl/index.rs":"1db1bee7074a3fe9668d2c2ba0bd5053d6443f4ea19a56e4cccf2aa2bc8a33c9","src/front/wgsl/lower/construction.rs":"e9797e34f6e8be3e32579bc162bb1d3f4a2fbf7450d9a54943c16a21b6f62a46","src/front/wgsl/lower/conversion.rs":"fa17798087afb61a2791b946c99854ad55580020969dc0820be9e14f10138de1","src/front/wgsl/lower/mod.rs":"bba9e6643174e03f398d3bac4a1fe71daab7f5abe12d1b76c3512d32c908fe68","src/front/wgsl/mod.rs":"435d381a2224682d60bc40f716a12ab00f239355e400a0d0cce40c055377cd73","src/front/wgsl/parse/ast.rs":"19c7ebafbecd29a795ea11e0e0f3c1a64d406594037ece7c3dce3670d808897a","src/front/wgsl/parse/conv.rs":"12a2d7199b09d0c95c7d5125b4987d7e54fc5811acb15d50978ab5c04beab916","src/front/wgsl/parse/directive.rs":"c96f33cef2c1d8a374fe1b3827538f7db33d6b7811a6e0914d29de80b8963257","src/front/wgsl/parse/directive/enable_extension.rs":"b076d8d90db1a374b4a49afbac522287f360c2d65e43db9103863e0a1c909557","src/front/wgsl/parse/directive/language_extension.rs":"b769f340f4f89410734d0d0fa3bc8530eb10ddb05bbd175da7bf61b062334cc0","src/front/wgsl/parse/lexer.rs":"a43060f637965045aabe3d6498ecf4b0bbc40aee4bb0156ca542b09b3c08178f","src/front/wgsl/parse/mod.rs":"8a86ed10bf881823b69394e6ec5568b83f387a960df4eef8bd611d7b86edc18a","src/front/wgsl/parse/number.rs":"7af92c71031e4c4258e9d8d323f7ee99a2fd4be3b6975ab9b8b53b95431845d9","src/front/wgsl/tests.rs":"a50bd5d6d218fc0be8876d1f9db7936ff593d93805c5e85754ae6bf277beae32","src/ir/block.rs":"b562a83a4fa53002d2ca21b4553ed8e2fa77f61e687f24fd4bbd90f1597b2a9d","src/ir/mod.rs":"df003df4951e5071186130d7676bce43b78fee445cace8a2a4002f5895546d56","src/keywords/mod.rs":"47a6fde012bf7d1e70f0fac7762f6a8e7dca6b9bbb99e2cada773c61527cfbfe","src/keywords/wgsl.rs":"60e8b7082d9df8c89544ad08ef7fdd8f7b75c48906cffb285fe4f53de04bb263","src/lib.rs":"c62861b9e3133d560034cdb7af9dea448c4c0d0da7b6ee2f1b69050571302c12","src/non_max_u32.rs":"b2d81efda0e1e5ace9e2fad990a7adf628f1dec63273b069c93d5423eb78350d","src/proc/constant_evaluator.rs":"48e5c4fb524b6f39af214b250008e992766a78c9efd9cab38bcd0d6fd3dc5978","src/proc/emitter.rs":"39ac886c651e2ad33c06a676a7e4826a0e93de0af660c01e8e4b1f7406742f88","src/proc/index.rs":"9a9db3e813b92d0751f9e6edfc8147a3c5bfd30ae76e7e97e8d083f94ab48f07","src/proc/layouter.rs":"bf50fed8cf4b15400218fa1cf897f04925795939b09c9ce658eb8bc7559a5705","src/proc/mod.rs":"e31a505075a99314bb1c4062870072198d32fa6a75a369676d9748099402084f","src/proc/namer.rs":"70ad6d5fa77b3d7713e738ab1625d9c8a6f243e89239c4c8772749321a0f2c56","src/proc/terminator.rs":"66b0082dce3b29d6d20e9a891c66524d756c8e567ac844d61355248ac4623b07","src/proc/type_methods.rs":"0e36c925a2d4f3534fcea9a083344e008335fab02c287ea30a1d060307b1fa7f","src/proc/typifier.rs":"2e54c6fbe1b9afdd4875555d04905460ab6932d712c1a977c4cb4c5c7a2f43cb","src/span.rs":"fed0e579ee9aa9dc48d4f333cf376963f179441691d17cc20a454ee64d5479a9","src/valid/analyzer.rs":"9c662636da86bd9c0d3f101b8023b184a6a4d23b9dabd6c2fda33fd1ea82e835","src/valid/compose.rs":"f27a6bc13180fcd1ecbbab698faab5e25ecf0115676fda6f367fb3e8fb72a6b0","src/valid/expression.rs":"3665fb3bfe6907d5b815d58de13bdd96ffbafaafea31669b9747c802df9bff91","src/valid/function.rs":"ebdde4878f3a73b2b4fa36e55b9325f1ea95bbafce1f073f27f286140424e277","src/valid/handles.rs":"2750068c629fcad7ed876c1fd4b117d2deba5d3c9e9d6ed686df2c1a79df1fa1","src/valid/interface.rs":"836716fb8bc86ea5f82dc93d2db85409b5490c593a3b8e88312fc513c277aa33","src/valid/mod.rs":"c59c91f3a6df8df47f827c98771bc7bd5b28219f5383bf681fe75c2b4beea3ad","src/valid/type.rs":"0947af787fcbd40ac74dc6fb35c1973879cbd548d1cb815eb0907c0a7bf7c362"},"package":null} \ No newline at end of file diff --git a/third_party/rust/naga/Cargo.toml b/third_party/rust/naga/Cargo.toml index 3a2c3702a503..52799900cb27 100644 --- a/third_party/rust/naga/Cargo.toml +++ b/third_party/rust/naga/Cargo.toml @@ -110,7 +110,12 @@ default-features = false version = "2.9" [dependencies.codespan-reporting] -version = "0.11.0" +version = "0.12" +features = [ + "std", + "termcolor", +] +default-features = false [dependencies.half] version = "2.5" @@ -165,9 +170,6 @@ features = ["derive"] optional = true default-features = false -[dependencies.termcolor] -version = "1.4.1" - [dependencies.thiserror] version = "2" default-features = false @@ -178,7 +180,6 @@ optional = true [dev-dependencies] diff = "0.1" -ron = "0.8.0" toml = "0.8" [dev-dependencies.env_logger] @@ -199,6 +200,9 @@ path = "./hlsl-snapshots" [dev-dependencies.itertools] version = "0.13.0" +[dev-dependencies.ron] +version = "0.9" + [dev-dependencies.rspirv] version = "0.11" git = "https://github.com/gfx-rs/rspirv" diff --git a/third_party/rust/naga/src/arena/mod.rs b/third_party/rust/naga/src/arena/mod.rs index 7a40b09b76d4..4fbfe7cc60e6 100644 --- a/third_party/rust/naga/src/arena/mod.rs +++ b/third_party/rust/naga/src/arena/mod.rs @@ -271,9 +271,7 @@ where D: serde::Deserializer<'de>, { let data = Vec::deserialize(deserializer)?; - let span_info = core::iter::repeat(Span::default()) - .take(data.len()) - .collect(); + let span_info = core::iter::repeat_n(Span::default(), data.len()).collect(); Ok(Self { data, span_info }) } diff --git a/third_party/rust/naga/src/arena/unique_arena.rs b/third_party/rust/naga/src/arena/unique_arena.rs index cdf2426801f4..c93fb6bb6874 100644 --- a/third_party/rust/naga/src/arena/unique_arena.rs +++ b/third_party/rust/naga/src/arena/unique_arena.rs @@ -224,9 +224,7 @@ where D: serde::Deserializer<'de>, { let set = FastIndexSet::deserialize(deserializer)?; - let span_info = core::iter::repeat(Span::default()) - .take(set.len()) - .collect(); + let span_info = core::iter::repeat_n(Span::default(), set.len()).collect(); Ok(Self { set, span_info }) } diff --git a/third_party/rust/naga/src/back/glsl/mod.rs b/third_party/rust/naga/src/back/glsl/mod.rs index 403cefd56658..256fceb5e18c 100644 --- a/third_party/rust/naga/src/back/glsl/mod.rs +++ b/third_party/rust/naga/src/back/glsl/mod.rs @@ -230,6 +230,30 @@ impl Version { fn supports_derivative_control(&self) -> bool { *self >= Version::Desktop(450) } + + // For supports_pack_unpack_4x8, supports_pack_unpack_snorm_2x16, supports_pack_unpack_unorm_2x16 + // see: + // https://registry.khronos.org/OpenGL-Refpages/gl4/html/unpackUnorm.xhtml + // https://registry.khronos.org/OpenGL-Refpages/es3/html/unpackUnorm.xhtml + // https://registry.khronos.org/OpenGL-Refpages/gl4/html/packUnorm.xhtml + // https://registry.khronos.org/OpenGL-Refpages/es3/html/packUnorm.xhtml + fn supports_pack_unpack_4x8(&self) -> bool { + *self >= Version::Desktop(400) || *self >= Version::new_gles(310) + } + fn supports_pack_unpack_snorm_2x16(&self) -> bool { + *self >= Version::Desktop(420) || *self >= Version::new_gles(300) + } + fn supports_pack_unpack_unorm_2x16(&self) -> bool { + *self >= Version::Desktop(400) || *self >= Version::new_gles(300) + } + + // https://registry.khronos.org/OpenGL-Refpages/gl4/html/unpackHalf2x16.xhtml + // https://registry.khronos.org/OpenGL-Refpages/gl4/html/packHalf2x16.xhtml + // https://registry.khronos.org/OpenGL-Refpages/es3/html/unpackHalf2x16.xhtml + // https://registry.khronos.org/OpenGL-Refpages/es3/html/packHalf2x16.xhtml + fn supports_pack_unpack_half_2x16(&self) -> bool { + *self >= Version::Desktop(420) || *self >= Version::new_gles(300) + } } impl PartialOrd for Version { @@ -1369,6 +1393,31 @@ impl<'a, W: Write> Writer<'a, W> { | crate::MathFunction::QuantizeToF16 => { self.need_bake_expressions.insert(arg); } + /* crate::MathFunction::Pack4x8unorm | */ + crate::MathFunction::Unpack4x8snorm + if !self.options.version.supports_pack_unpack_4x8() => + { + // We have a fallback if the platform doesn't natively support these + self.need_bake_expressions.insert(arg); + } + /* crate::MathFunction::Pack4x8unorm | */ + crate::MathFunction::Unpack4x8unorm + if !self.options.version.supports_pack_unpack_4x8() => + { + self.need_bake_expressions.insert(arg); + } + /* crate::MathFunction::Pack2x16snorm | */ + crate::MathFunction::Unpack2x16snorm + if !self.options.version.supports_pack_unpack_snorm_2x16() => + { + self.need_bake_expressions.insert(arg); + } + /* crate::MathFunction::Pack2x16unorm | */ + crate::MathFunction::Unpack2x16unorm + if !self.options.version.supports_pack_unpack_unorm_2x16() => + { + self.need_bake_expressions.insert(arg); + } crate::MathFunction::ExtractBits => { // Only argument 1 is re-used. self.need_bake_expressions.insert(arg1.unwrap()); @@ -3756,11 +3805,43 @@ impl<'a, W: Write> Writer<'a, W> { Mf::FirstTrailingBit => "findLSB", Mf::FirstLeadingBit => "findMSB", // data packing - Mf::Pack4x8snorm => "packSnorm4x8", - Mf::Pack4x8unorm => "packUnorm4x8", - Mf::Pack2x16snorm => "packSnorm2x16", - Mf::Pack2x16unorm => "packUnorm2x16", - Mf::Pack2x16float => "packHalf2x16", + Mf::Pack4x8snorm => { + if self.options.version.supports_pack_unpack_4x8() { + "packSnorm4x8" + } else { + // polyfill should go here. Needs a corresponding entry in `need_bake_expression` + return Err(Error::UnsupportedExternal("packSnorm4x8".into())); + } + } + Mf::Pack4x8unorm => { + if self.options.version.supports_pack_unpack_4x8() { + "packUnorm4x8" + } else { + return Err(Error::UnsupportedExternal("packUnorm4x8".to_owned())); + } + } + Mf::Pack2x16snorm => { + if self.options.version.supports_pack_unpack_snorm_2x16() { + "packSnorm2x16" + } else { + return Err(Error::UnsupportedExternal("packSnorm2x16".to_owned())); + } + } + Mf::Pack2x16unorm => { + if self.options.version.supports_pack_unpack_unorm_2x16() { + "packUnorm2x16" + } else { + return Err(Error::UnsupportedExternal("packUnorm2x16".to_owned())); + } + } + Mf::Pack2x16float => { + if self.options.version.supports_pack_unpack_half_2x16() { + "packHalf2x16" + } else { + return Err(Error::UnsupportedExternal("packHalf2x16".to_owned())); + } + } + fun @ (Mf::Pack4xI8 | Mf::Pack4xU8) => { let was_signed = match fun { Mf::Pack4xI8 => true, @@ -3787,11 +3868,77 @@ impl<'a, W: Write> Writer<'a, W> { return Ok(()); } // data unpacking - Mf::Unpack4x8snorm => "unpackSnorm4x8", - Mf::Unpack4x8unorm => "unpackUnorm4x8", - Mf::Unpack2x16snorm => "unpackSnorm2x16", - Mf::Unpack2x16unorm => "unpackUnorm2x16", - Mf::Unpack2x16float => "unpackHalf2x16", + Mf::Unpack2x16float => { + if self.options.version.supports_pack_unpack_half_2x16() { + "unpackHalf2x16" + } else { + return Err(Error::UnsupportedExternal("unpackHalf2x16".into())); + } + } + Mf::Unpack2x16snorm => { + if self.options.version.supports_pack_unpack_snorm_2x16() { + "unpackSnorm2x16" + } else { + let scale = 32767; + + write!(self.out, "(vec2(ivec2(")?; + self.write_expr(arg, ctx)?; + write!(self.out, " << 16, ")?; + self.write_expr(arg, ctx)?; + write!(self.out, ") >> 16) / {scale}.0)")?; + return Ok(()); + } + } + Mf::Unpack2x16unorm => { + if self.options.version.supports_pack_unpack_unorm_2x16() { + "unpackUnorm2x16" + } else { + let scale = 65535; + + write!(self.out, "(vec2(")?; + self.write_expr(arg, ctx)?; + write!(self.out, " & 0xFFFFu, ")?; + self.write_expr(arg, ctx)?; + write!(self.out, " >> 16) / {scale}.0)")?; + return Ok(()); + } + } + Mf::Unpack4x8snorm => { + if self.options.version.supports_pack_unpack_4x8() { + "unpackSnorm4x8" + } else { + let scale = 127; + + write!(self.out, "(vec4(ivec4(")?; + self.write_expr(arg, ctx)?; + write!(self.out, " << 24, ")?; + self.write_expr(arg, ctx)?; + write!(self.out, " << 16, ")?; + self.write_expr(arg, ctx)?; + write!(self.out, " << 8, ")?; + self.write_expr(arg, ctx)?; + write!(self.out, ") >> 24) / {scale}.0)")?; + return Ok(()); + } + } + Mf::Unpack4x8unorm => { + if self.options.version.supports_pack_unpack_4x8() { + "unpackUnorm4x8" + } else { + let scale = 255; + + write!(self.out, "(vec4(")?; + self.write_expr(arg, ctx)?; + write!(self.out, " & 0xFFu, ")?; + self.write_expr(arg, ctx)?; + write!(self.out, " >> 8 & 0xFFu, ")?; + self.write_expr(arg, ctx)?; + write!(self.out, " >> 16 & 0xFFu, ")?; + self.write_expr(arg, ctx)?; + write!(self.out, " >> 24) / {scale}.0)")?; + return Ok(()); + } + } fun @ (Mf::Unpack4xI8 | Mf::Unpack4xU8) => { let sign_prefix = match fun { Mf::Unpack4xI8 => 'i', diff --git a/third_party/rust/naga/src/back/hlsl/help.rs b/third_party/rust/naga/src/back/hlsl/help.rs index 00d64faa4dd1..058d3969237c 100644 --- a/third_party/rust/naga/src/back/hlsl/help.rs +++ b/third_party/rust/naga/src/back/hlsl/help.rs @@ -32,8 +32,8 @@ use core::fmt::Write; use super::{ super::FunctionCtx, writer::{ - ABS_FUNCTION, DIV_FUNCTION, EXTRACT_BITS_FUNCTION, INSERT_BITS_FUNCTION, MOD_FUNCTION, - NEG_FUNCTION, + ABS_FUNCTION, DIV_FUNCTION, EXTRACT_BITS_FUNCTION, F2I32_FUNCTION, F2I64_FUNCTION, + F2U32_FUNCTION, F2U64_FUNCTION, INSERT_BITS_FUNCTION, MOD_FUNCTION, NEG_FUNCTION, }, BackendResult, WrappedType, }; @@ -97,6 +97,15 @@ pub(super) struct WrappedBinaryOp { pub(super) right_ty: (Option, crate::Scalar), } +#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)] +pub(super) struct WrappedCast { + // This can only represent scalar or vector types. If we ever need to wrap + // casts with other types, we'll need a better representation. + pub(super) vector_size: Option, + pub(super) src_scalar: crate::Scalar, + pub(super) dst_scalar: crate::Scalar, +} + /// HLSL backend requires its own `ImageQuery` enum. /// /// It is used inside `WrappedImageQuery` and should be unique per ImageQuery function. @@ -1355,6 +1364,97 @@ impl super::Writer<'_, W> { Ok(()) } + fn write_wrapped_cast_functions( + &mut self, + module: &crate::Module, + func_ctx: &FunctionCtx, + ) -> BackendResult { + for (_, expression) in func_ctx.expressions.iter() { + if let crate::Expression::As { + expr, + kind, + convert: Some(width), + } = *expression + { + // Avoid undefined behaviour when casting from a float to integer + // when the value is out of range for the target type. Additionally + // ensure we clamp to the correct value as per the WGSL spec. + // + // https://www.w3.org/TR/WGSL/#floating-point-conversion: + // * If X is exactly representable in the target type T, then the + // result is that value. + // * Otherwise, the result is the value in T closest to + // truncate(X) and also exactly representable in the original + // floating point type. + let src_ty = func_ctx.resolve_type(expr, &module.types); + let Some((vector_size, src_scalar)) = src_ty.vector_size_and_scalar() else { + continue; + }; + let dst_scalar = crate::Scalar { kind, width }; + if src_scalar.kind != ScalarKind::Float + || (dst_scalar.kind != ScalarKind::Sint && dst_scalar.kind != ScalarKind::Uint) + { + continue; + } + + let wrapped = WrappedCast { + src_scalar, + vector_size, + dst_scalar, + }; + if !self.wrapped.insert(WrappedType::Cast(wrapped)) { + continue; + } + + let (src_ty, dst_ty) = match vector_size { + None => ( + crate::TypeInner::Scalar(src_scalar), + crate::TypeInner::Scalar(dst_scalar), + ), + Some(vector_size) => ( + crate::TypeInner::Vector { + scalar: src_scalar, + size: vector_size, + }, + crate::TypeInner::Vector { + scalar: dst_scalar, + size: vector_size, + }, + ), + }; + let (min, max) = + crate::proc::min_max_float_representable_by(src_scalar, dst_scalar); + let cast_str = format!( + "{}{}", + dst_scalar.to_hlsl_str()?, + vector_size + .map(crate::common::vector_size_str) + .unwrap_or(""), + ); + let fun_name = match dst_scalar { + crate::Scalar::I32 => F2I32_FUNCTION, + crate::Scalar::U32 => F2U32_FUNCTION, + crate::Scalar::I64 => F2I64_FUNCTION, + crate::Scalar::U64 => F2U64_FUNCTION, + _ => unreachable!(), + }; + self.write_value_type(module, &dst_ty)?; + write!(self.out, " {fun_name}(")?; + self.write_value_type(module, &src_ty)?; + writeln!(self.out, " value) {{")?; + let level = crate::back::Level(1); + write!(self.out, "{level}return {cast_str}(clamp(value, ")?; + self.write_literal(min)?; + write!(self.out, ", ")?; + self.write_literal(max)?; + writeln!(self.out, "));",)?; + writeln!(self.out, "}}")?; + writeln!(self.out)?; + } + } + Ok(()) + } + /// Helper function that writes various wrapped functions pub(super) fn write_wrapped_functions( &mut self, @@ -1366,6 +1466,7 @@ impl super::Writer<'_, W> { self.write_wrapped_binary_ops(module, func_ctx)?; self.write_wrapped_expression_functions(module, func_ctx.expressions, Some(func_ctx))?; self.write_wrapped_zero_value_functions(module, func_ctx.expressions)?; + self.write_wrapped_cast_functions(module, func_ctx)?; for (handle, _) in func_ctx.expressions.iter() { match func_ctx.expressions[handle] { diff --git a/third_party/rust/naga/src/back/hlsl/keywords.rs b/third_party/rust/naga/src/back/hlsl/keywords.rs index ac28797d8b96..d3b9ad8cf0d2 100644 --- a/third_party/rust/naga/src/back/hlsl/keywords.rs +++ b/third_party/rust/naga/src/back/hlsl/keywords.rs @@ -830,6 +830,10 @@ pub const RESERVED: &[&str] = &[ super::writer::DIV_FUNCTION, super::writer::MOD_FUNCTION, super::writer::NEG_FUNCTION, + super::writer::F2I32_FUNCTION, + super::writer::F2U32_FUNCTION, + super::writer::F2I64_FUNCTION, + super::writer::F2U64_FUNCTION, ]; // DXC scalar types, from https://github.com/microsoft/DirectXShaderCompiler/blob/18c9e114f9c314f93e68fbc72ce207d4ed2e65ae/tools/clang/lib/AST/ASTContextHLSL.cpp#L48-L254 diff --git a/third_party/rust/naga/src/back/hlsl/mod.rs b/third_party/rust/naga/src/back/hlsl/mod.rs index 60a4bb4857b9..9e041ff73f8e 100644 --- a/third_party/rust/naga/src/back/hlsl/mod.rs +++ b/third_party/rust/naga/src/back/hlsl/mod.rs @@ -462,6 +462,7 @@ enum WrappedType { Math(help::WrappedMath), UnaryOp(help::WrappedUnaryOp), BinaryOp(help::WrappedBinaryOp), + Cast(help::WrappedCast), } #[derive(Default)] diff --git a/third_party/rust/naga/src/back/hlsl/writer.rs b/third_party/rust/naga/src/back/hlsl/writer.rs index c37234244988..dd79174ac61f 100644 --- a/third_party/rust/naga/src/back/hlsl/writer.rs +++ b/third_party/rust/naga/src/back/hlsl/writer.rs @@ -38,6 +38,10 @@ pub(crate) const ABS_FUNCTION: &str = "naga_abs"; pub(crate) const DIV_FUNCTION: &str = "naga_div"; pub(crate) const MOD_FUNCTION: &str = "naga_mod"; pub(crate) const NEG_FUNCTION: &str = "naga_neg"; +pub(crate) const F2I32_FUNCTION: &str = "naga_f2i32"; +pub(crate) const F2U32_FUNCTION: &str = "naga_f2u32"; +pub(crate) const F2I64_FUNCTION: &str = "naga_f2i64"; +pub(crate) const F2U64_FUNCTION: &str = "naga_f2u64"; struct EpStructMember { name: String, @@ -170,12 +174,14 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { } let loop_bound_name = self.namer.call("loop_bound"); - let decl = format!("{level}uint2 {loop_bound_name} = uint2(0u, 0u);"); - let level = level.next(); let max = u32::MAX; + // Count down from u32::MAX rather than up from 0 to avoid hang on + // certain Intel drivers. See . + let decl = format!("{level}uint2 {loop_bound_name} = uint2({max}u, {max}u);"); + let level = level.next(); let break_and_inc = format!( - "{level}if (all({loop_bound_name} == uint2({max}u, {max}u))) {{ break; }} -{level}{loop_bound_name} += uint2({loop_bound_name}.y == {max}u, 1u);" + "{level}if (all({loop_bound_name} == uint2(0u, 0u))) {{ break; }} +{level}{loop_bound_name} -= uint2({loop_bound_name}.y == 0u, 1u);" ); Some((decl, break_and_inc)) @@ -2610,6 +2616,28 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { }) } + pub(super) fn write_literal(&mut self, literal: crate::Literal) -> BackendResult { + match literal { + crate::Literal::F64(value) => write!(self.out, "{value:?}L")?, + crate::Literal::F32(value) => write!(self.out, "{value:?}")?, + crate::Literal::F16(value) => write!(self.out, "{value:?}h")?, + crate::Literal::U32(value) => write!(self.out, "{value}u")?, + // HLSL has no suffix for explicit i32 literals, but not using any suffix + // makes the type ambiguous which prevents overload resolution from + // working. So we explicitly use the int() constructor syntax. + crate::Literal::I32(value) => write!(self.out, "int({value})")?, + crate::Literal::U64(value) => write!(self.out, "{value}uL")?, + crate::Literal::I64(value) => write!(self.out, "{value}L")?, + crate::Literal::Bool(value) => write!(self.out, "{value}")?, + crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => { + return Err(Error::Custom( + "Abstract types should not appear in IR presented to backends".into(), + )); + } + } + Ok(()) + } + fn write_possibly_const_expression( &mut self, module: &Module, @@ -2623,26 +2651,9 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { use crate::Expression; match expressions[expr] { - Expression::Literal(literal) => match literal { - // Floats are written using `Debug` instead of `Display` because it always appends the - // decimal part even it's zero - crate::Literal::F64(value) => write!(self.out, "{value:?}L")?, - crate::Literal::F32(value) => write!(self.out, "{value:?}")?, - crate::Literal::F16(value) => write!(self.out, "{value:?}h")?, - crate::Literal::U32(value) => write!(self.out, "{value}u")?, - // HLSL has no suffix for explicit i32 literals, but not using any suffix - // makes the type ambiguous which prevents overload resolution from - // working. So we explicitly use the int() constructor syntax. - crate::Literal::I32(value) => write!(self.out, "int({value})")?, - crate::Literal::U64(value) => write!(self.out, "{value}uL")?, - crate::Literal::I64(value) => write!(self.out, "{value}L")?, - crate::Literal::Bool(value) => write!(self.out, "{value}")?, - crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => { - return Err(Error::Custom( - "Abstract types should not appear in IR presented to backends".into(), - )); - } - }, + Expression::Literal(literal) => { + self.write_literal(literal)?; + } Expression::Constant(handle) => { let constant = &module.constants[handle]; if constant.name.is_some() { @@ -3318,53 +3329,72 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { convert, } => { let inner = func_ctx.resolve_type(expr, &module.types); - let close_paren = match convert { - Some(dst_width) => { - let scalar = Scalar { - kind, - width: dst_width, - }; - match *inner { - TypeInner::Vector { size, .. } => { - write!( - self.out, - "{}{}(", - scalar.to_hlsl_str()?, - common::vector_size_str(size) - )?; - } - TypeInner::Scalar(_) => { - write!(self.out, "{}(", scalar.to_hlsl_str()?,)?; - } - TypeInner::Matrix { columns, rows, .. } => { - write!( - self.out, - "{}{}x{}(", - scalar.to_hlsl_str()?, - common::vector_size_str(columns), - common::vector_size_str(rows) - )?; - } - _ => { - return Err(Error::Unimplemented(format!( - "write_expr expression::as {inner:?}" - ))); - } - }; - true - } - None => { - if inner.scalar_width() == Some(8) { - false - } else { - write!(self.out, "{}(", kind.to_hlsl_cast(),)?; + if inner.scalar_kind() == Some(ScalarKind::Float) + && (kind == ScalarKind::Sint || kind == ScalarKind::Uint) + && convert.is_some() + { + // Use helper functions for float to int casts in order to + // avoid undefined behaviour when value is out of range for + // the target type. + let fun_name = match (kind, convert) { + (ScalarKind::Sint, Some(4)) => F2I32_FUNCTION, + (ScalarKind::Uint, Some(4)) => F2U32_FUNCTION, + (ScalarKind::Sint, Some(8)) => F2I64_FUNCTION, + (ScalarKind::Uint, Some(8)) => F2U64_FUNCTION, + _ => unreachable!(), + }; + write!(self.out, "{fun_name}(")?; + self.write_expr(module, expr, func_ctx)?; + write!(self.out, ")")?; + } else { + let close_paren = match convert { + Some(dst_width) => { + let scalar = Scalar { + kind, + width: dst_width, + }; + match *inner { + TypeInner::Vector { size, .. } => { + write!( + self.out, + "{}{}(", + scalar.to_hlsl_str()?, + common::vector_size_str(size) + )?; + } + TypeInner::Scalar(_) => { + write!(self.out, "{}(", scalar.to_hlsl_str()?,)?; + } + TypeInner::Matrix { columns, rows, .. } => { + write!( + self.out, + "{}{}x{}(", + scalar.to_hlsl_str()?, + common::vector_size_str(columns), + common::vector_size_str(rows) + )?; + } + _ => { + return Err(Error::Unimplemented(format!( + "write_expr expression::as {inner:?}" + ))); + } + }; true } + None => { + if inner.scalar_width() == Some(8) { + false + } else { + write!(self.out, "{}(", kind.to_hlsl_cast(),)?; + true + } + } + }; + self.write_expr(module, expr, func_ctx)?; + if close_paren { + write!(self.out, ")")?; } - }; - self.write_expr(module, expr, func_ctx)?; - if close_paren { - write!(self.out, ")")?; } } Expression::Math { diff --git a/third_party/rust/naga/src/back/msl/keywords.rs b/third_party/rust/naga/src/back/msl/keywords.rs index 2228ef176484..4feb6004fc03 100644 --- a/third_party/rust/naga/src/back/msl/keywords.rs +++ b/third_party/rust/naga/src/back/msl/keywords.rs @@ -349,6 +349,10 @@ pub const RESERVED: &[&str] = &[ super::writer::DIV_FUNCTION, super::writer::MOD_FUNCTION, super::writer::NEG_FUNCTION, + super::writer::F2I32_FUNCTION, + super::writer::F2U32_FUNCTION, + super::writer::F2I64_FUNCTION, + super::writer::F2U64_FUNCTION, super::writer::ARGUMENT_BUFFER_WRAPPER_STRUCT, ]; diff --git a/third_party/rust/naga/src/back/msl/writer.rs b/third_party/rust/naga/src/back/msl/writer.rs index 0c4f219a1b16..83b8bd43d7dd 100644 --- a/third_party/rust/naga/src/back/msl/writer.rs +++ b/third_party/rust/naga/src/back/msl/writer.rs @@ -52,6 +52,10 @@ pub(crate) const ABS_FUNCTION: &str = "naga_abs"; pub(crate) const DIV_FUNCTION: &str = "naga_div"; pub(crate) const MOD_FUNCTION: &str = "naga_mod"; pub(crate) const NEG_FUNCTION: &str = "naga_neg"; +pub(crate) const F2I32_FUNCTION: &str = "naga_f2i32"; +pub(crate) const F2U32_FUNCTION: &str = "naga_f2u32"; +pub(crate) const F2I64_FUNCTION: &str = "naga_f2i64"; +pub(crate) const F2U64_FUNCTION: &str = "naga_f2u64"; /// For some reason, Metal does not let you have `metal::texture<..>*` as a buffer argument. /// However, if you put that texture inside a struct, everything is totally fine. This /// baffles me to no end. @@ -405,6 +409,11 @@ enum WrappedFunction { fun: crate::MathFunction, arg_ty: (Option, crate::Scalar), }, + Cast { + src_scalar: crate::Scalar, + vector_size: Option, + dst_scalar: crate::Scalar, + }, } pub struct Writer { @@ -850,12 +859,13 @@ impl Writer { } let loop_bound_name = self.namer.call("loop_bound"); - let decl = format!("{level}uint2 {loop_bound_name} = uint2(0u);"); + // Count down from u32::MAX rather than up from 0 to avoid hang on + // certain Intel drivers. See . + let decl = format!("{level}uint2 {loop_bound_name} = uint2({}u);", u32::MAX); let level = level.next(); - let max = u32::MAX; let break_and_inc = format!( - "{level}if ({NAMESPACE}::all({loop_bound_name} == uint2({max}u))) {{ break; }} -{level}{loop_bound_name} += uint2({loop_bound_name}.y == {max}u, 1u);" + "{level}if ({NAMESPACE}::all({loop_bound_name} == uint2(0u))) {{ break; }} +{level}{loop_bound_name} -= uint2({loop_bound_name}.y == 0u, 1u);" ); Some((decl, break_and_inc)) @@ -1448,6 +1458,61 @@ impl Writer { ) } + fn put_literal(&mut self, literal: crate::Literal) -> BackendResult { + match literal { + crate::Literal::F64(_) => { + return Err(Error::CapabilityNotSupported(valid::Capabilities::FLOAT64)) + } + crate::Literal::F16(value) => { + if value.is_infinite() { + let sign = if value.is_sign_negative() { "-" } else { "" }; + write!(self.out, "{sign}INFINITY")?; + } else if value.is_nan() { + write!(self.out, "NAN")?; + } else { + let suffix = if value.fract() == f16::from_f32(0.0) { + ".0h" + } else { + "h" + }; + write!(self.out, "{value}{suffix}")?; + } + } + crate::Literal::F32(value) => { + if value.is_infinite() { + let sign = if value.is_sign_negative() { "-" } else { "" }; + write!(self.out, "{sign}INFINITY")?; + } else if value.is_nan() { + write!(self.out, "NAN")?; + } else { + let suffix = if value.fract() == 0.0 { ".0" } else { "" }; + write!(self.out, "{value}{suffix}")?; + } + } + crate::Literal::U32(value) => { + write!(self.out, "{value}u")?; + } + crate::Literal::I32(value) => { + write!(self.out, "{value}")?; + } + crate::Literal::U64(value) => { + write!(self.out, "{value}uL")?; + } + crate::Literal::I64(value) => { + write!(self.out, "{value}L")?; + } + crate::Literal::Bool(value) => { + write!(self.out, "{value}")?; + } + crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => { + return Err(Error::GenericValidation( + "Unsupported abstract literal".into(), + )); + } + } + Ok(()) + } + #[allow(clippy::too_many_arguments)] fn put_possibly_const_expression( &mut self, @@ -1464,57 +1529,9 @@ impl Writer { E: Fn(&mut Self, &C, Handle) -> BackendResult, { match expressions[expr_handle] { - crate::Expression::Literal(literal) => match literal { - crate::Literal::F64(_) => { - return Err(Error::CapabilityNotSupported(valid::Capabilities::FLOAT64)) - } - crate::Literal::F16(value) => { - if value.is_infinite() { - let sign = if value.is_sign_negative() { "-" } else { "" }; - write!(self.out, "{sign}INFINITY")?; - } else if value.is_nan() { - write!(self.out, "NAN")?; - } else { - let suffix = if value.fract() == f16::from_f32(0.0) { - ".0h" - } else { - "h" - }; - write!(self.out, "{value}{suffix}")?; - } - } - crate::Literal::F32(value) => { - if value.is_infinite() { - let sign = if value.is_sign_negative() { "-" } else { "" }; - write!(self.out, "{sign}INFINITY")?; - } else if value.is_nan() { - write!(self.out, "NAN")?; - } else { - let suffix = if value.fract() == 0.0 { ".0" } else { "" }; - write!(self.out, "{value}{suffix}")?; - } - } - crate::Literal::U32(value) => { - write!(self.out, "{value}u")?; - } - crate::Literal::I32(value) => { - write!(self.out, "{value}")?; - } - crate::Literal::U64(value) => { - write!(self.out, "{value}uL")?; - } - crate::Literal::I64(value) => { - write!(self.out, "{value}L")?; - } - crate::Literal::Bool(value) => { - write!(self.out, "{value}")?; - } - crate::Literal::AbstractInt(_) | crate::Literal::AbstractFloat(_) => { - return Err(Error::GenericValidation( - "Unsupported abstract literal".into(), - )); - } - }, + crate::Expression::Literal(literal) => { + self.put_literal(literal)?; + } crate::Expression::Constant(handle) => { let constant = &module.constants[handle]; if constant.name.is_some() { @@ -2314,24 +2331,43 @@ impl Writer { convert, } => match *context.resolve_type(expr) { crate::TypeInner::Scalar(src) | crate::TypeInner::Vector { scalar: src, .. } => { - let target_scalar = crate::Scalar { - kind, - width: convert.unwrap_or(src.width), - }; - let op = match convert { - Some(_) => "static_cast", - None => "as_type", - }; - write!(self.out, "{op}<")?; - match *context.resolve_type(expr) { - crate::TypeInner::Vector { size, .. } => { - put_numeric_type(&mut self.out, target_scalar, &[size])? - } - _ => put_numeric_type(&mut self.out, target_scalar, &[])?, - }; - write!(self.out, ">(")?; - self.put_expression(expr, context, true)?; - write!(self.out, ")")?; + if src.kind == crate::ScalarKind::Float + && (kind == crate::ScalarKind::Sint || kind == crate::ScalarKind::Uint) + && convert.is_some() + { + // Use helper functions for float to int casts in order to avoid + // undefined behaviour when value is out of range for the target + // type. + let fun_name = match (kind, convert) { + (crate::ScalarKind::Sint, Some(4)) => F2I32_FUNCTION, + (crate::ScalarKind::Uint, Some(4)) => F2U32_FUNCTION, + (crate::ScalarKind::Sint, Some(8)) => F2I64_FUNCTION, + (crate::ScalarKind::Uint, Some(8)) => F2U64_FUNCTION, + _ => unreachable!(), + }; + write!(self.out, "{fun_name}(")?; + self.put_expression(expr, context, true)?; + write!(self.out, ")")?; + } else { + let target_scalar = crate::Scalar { + kind, + width: convert.unwrap_or(src.width), + }; + let op = match convert { + Some(_) => "static_cast", + None => "as_type", + }; + write!(self.out, "{op}<")?; + match *context.resolve_type(expr) { + crate::TypeInner::Vector { size, .. } => { + put_numeric_type(&mut self.out, target_scalar, &[size])? + } + _ => put_numeric_type(&mut self.out, target_scalar, &[])?, + }; + write!(self.out, ">(")?; + self.put_expression(expr, context, true)?; + write!(self.out, ")")?; + } } crate::TypeInner::Matrix { columns, @@ -5343,6 +5379,76 @@ template _ => {} } } + crate::Expression::As { + expr, + kind, + convert: Some(width), + } => { + // Avoid undefined behaviour when casting from a float to integer + // when the value is out of range for the target type. Additionally + // ensure we clamp to the correct value as per the WGSL spec. + // + // https://www.w3.org/TR/WGSL/#floating-point-conversion: + // * If X is exactly representable in the target type T, then the + // result is that value. + // * Otherwise, the result is the value in T closest to + // truncate(X) and also exactly representable in the original + // floating point type. + let src_ty = func_ctx.resolve_type(expr, &module.types); + let Some((vector_size, src_scalar)) = src_ty.vector_size_and_scalar() else { + continue; + }; + let dst_scalar = crate::Scalar { kind, width }; + if src_scalar.kind != crate::ScalarKind::Float + || (dst_scalar.kind != crate::ScalarKind::Sint + && dst_scalar.kind != crate::ScalarKind::Uint) + { + continue; + } + let wrapped = WrappedFunction::Cast { + src_scalar, + vector_size, + dst_scalar, + }; + if !self.wrapped_functions.insert(wrapped) { + continue; + } + let (min, max) = proc::min_max_float_representable_by(src_scalar, dst_scalar); + + let mut src_type_name = String::new(); + match vector_size { + None => put_numeric_type(&mut src_type_name, src_scalar, &[])?, + Some(size) => put_numeric_type(&mut src_type_name, src_scalar, &[size])?, + }; + let mut dst_type_name = String::new(); + match vector_size { + None => put_numeric_type(&mut dst_type_name, dst_scalar, &[])?, + Some(size) => put_numeric_type(&mut dst_type_name, dst_scalar, &[size])?, + }; + let fun_name = match dst_scalar { + crate::Scalar::I32 => F2I32_FUNCTION, + crate::Scalar::U32 => F2U32_FUNCTION, + crate::Scalar::I64 => F2I64_FUNCTION, + crate::Scalar::U64 => F2U64_FUNCTION, + _ => unreachable!(), + }; + + writeln!( + self.out, + "{dst_type_name} {fun_name}({src_type_name} value) {{" + )?; + let level = back::Level(1); + write!( + self.out, + "{level}return static_cast<{dst_type_name}>({NAMESPACE}::clamp(value, " + )?; + self.put_literal(min)?; + write!(self.out, ", ")?; + self.put_literal(max)?; + writeln!(self.out, "));")?; + writeln!(self.out, "}}")?; + writeln!(self.out)?; + } _ => {} } } diff --git a/third_party/rust/naga/src/back/spv/block.rs b/third_party/rust/naga/src/back/spv/block.rs index 858cc2b71a09..f021dc6d8137 100644 --- a/third_party/rust/naga/src/back/spv/block.rs +++ b/third_party/rust/naga/src/back/spv/block.rs @@ -310,7 +310,7 @@ impl BlockContext<'_> { uint2_ptr_type_id, loop_counter_var_id, spirv::StorageClass::Function, - Some(zero_uint2_const_id), + Some(max_uint2_const_id), ), }; self.function.force_loop_bounding_vars.push(var); @@ -331,14 +331,14 @@ impl BlockContext<'_> { None, )); - // If both the high and low u32s have reached u32::MAX then break. ie - // if (all(eq(loop_counter, vec2(u32::MAX)))) { break; } + // If both the high and low u32s have reached 0 then break. ie + // if (all(eq(loop_counter, vec2(0)))) { break; } let eq_id = self.gen_id(); block.body.push(Instruction::binary( spirv::Op::IEqual, bool2_type_id, eq_id, - max_uint2_const_id, + zero_uint2_const_id, load_id, )); let all_eq_id = self.gen_id(); @@ -360,9 +360,11 @@ impl BlockContext<'_> { ); block = Block::new(inc_counter_block_id); - // To simulate a 64-bit counter we always increment the low u32, and increment + // To simulate a 64-bit counter we always decrement the low u32, and decrement // the high u32 when the low u32 overflows. ie - // counter += vec2(select(0u, 1u, counter.y == u32::MAX), 1u); + // counter -= vec2(select(0u, 1u, counter.y == 0), 1u); + // Count down from u32::MAX rather than up from 0 to avoid hang on + // certain Intel drivers. See . let low_id = self.gen_id(); block.body.push(Instruction::composite_extract( uint_type_id, @@ -376,7 +378,7 @@ impl BlockContext<'_> { bool_type_id, low_overflow_id, low_id, - max_uint_const_id, + zero_uint_const_id, )); let carry_bit_id = self.gen_id(); block.body.push(Instruction::select( @@ -386,19 +388,19 @@ impl BlockContext<'_> { one_uint_const_id, zero_uint_const_id, )); - let increment_id = self.gen_id(); + let decrement_id = self.gen_id(); block.body.push(Instruction::composite_construct( uint2_type_id, - increment_id, + decrement_id, &[carry_bit_id, one_uint_const_id], )); let result_id = self.gen_id(); block.body.push(Instruction::binary( - spirv::Op::IAdd, + spirv::Op::ISub, uint2_type_id, result_id, load_id, - increment_id, + decrement_id, )); block .body @@ -1874,10 +1876,10 @@ impl BlockContext<'_> { }; enum Cast { - Identity, - Unary(spirv::Op), - Binary(spirv::Op, Word), - Ternary(spirv::Op, Word, Word), + Identity(Word), + Unary(spirv::Op, Word), + Binary(spirv::Op, Word, Word), + Ternary(spirv::Op, Word, Word, Word), } let cast = match (src_scalar.kind, kind, convert) { // Filter out identity casts. Some Adreno drivers are @@ -1886,10 +1888,10 @@ impl BlockContext<'_> { if src_kind == kind && convert.filter(|&width| width != src_scalar.width).is_none() => { - Cast::Identity + Cast::Identity(expr_id) } - (Sk::Bool, Sk::Bool, _) => Cast::Unary(spirv::Op::CopyObject), - (_, _, None) => Cast::Unary(spirv::Op::Bitcast), + (Sk::Bool, Sk::Bool, _) => Cast::Unary(spirv::Op::CopyObject, expr_id), + (_, _, None) => Cast::Unary(spirv::Op::Bitcast, expr_id), // casting to a bool - generate `OpXxxNotEqual` (_, Sk::Bool, Some(_)) => { let op = match src_scalar.kind { @@ -1914,7 +1916,7 @@ impl BlockContext<'_> { None => zero_scalar_id, }; - Cast::Binary(op, zero_id) + Cast::Binary(op, expr_id, zero_id) } // casting from a bool - generate `OpSelect` (Sk::Bool, _, Some(dst_width)) => { @@ -1946,60 +1948,99 @@ impl BlockContext<'_> { None => (one_scalar_id, zero_scalar_id), }; - Cast::Ternary(spirv::Op::Select, accept_id, reject_id) + Cast::Ternary(spirv::Op::Select, expr_id, accept_id, reject_id) + } + // Avoid undefined behaviour when casting from a float to integer + // when the value is out of range for the target type. Additionally + // ensure we clamp to the correct value as per the WGSL spec. + // + // https://www.w3.org/TR/WGSL/#floating-point-conversion: + // * If X is exactly representable in the target type T, then the + // result is that value. + // * Otherwise, the result is the value in T closest to + // truncate(X) and also exactly representable in the original + // floating point type. + (Sk::Float, Sk::Sint | Sk::Uint, Some(width)) => { + let dst_scalar = crate::Scalar { kind, width }; + let (min, max) = + crate::proc::min_max_float_representable_by(src_scalar, dst_scalar); + let expr_type_id = self.get_expression_type_id(&self.fun_info[expr].ty); + + let maybe_splat_const = |writer: &mut Writer, const_id| match src_size { + None => const_id, + Some(size) => { + let constituent_ids = [const_id; crate::VectorSize::MAX]; + writer.get_constant_composite( + LookupType::Local(LocalType::Numeric(NumericType::Vector { + size, + scalar: src_scalar, + })), + &constituent_ids[..size as usize], + ) + } + }; + let min_const_id = self.writer.get_constant_scalar(min); + let min_const_id = maybe_splat_const(self.writer, min_const_id); + let max_const_id = self.writer.get_constant_scalar(max); + let max_const_id = maybe_splat_const(self.writer, max_const_id); + + let clamp_id = self.gen_id(); + block.body.push(Instruction::ext_inst( + self.writer.gl450_ext_inst_id, + spirv::GLOp::FClamp, + expr_type_id, + clamp_id, + &[expr_id, min_const_id, max_const_id], + )); + + let op = match dst_scalar.kind { + crate::ScalarKind::Sint => spirv::Op::ConvertFToS, + crate::ScalarKind::Uint => spirv::Op::ConvertFToU, + _ => unreachable!(), + }; + Cast::Unary(op, clamp_id) } - (Sk::Float, Sk::Uint, Some(_)) => Cast::Unary(spirv::Op::ConvertFToU), - (Sk::Float, Sk::Sint, Some(_)) => Cast::Unary(spirv::Op::ConvertFToS), (Sk::Float, Sk::Float, Some(dst_width)) if src_scalar.width != dst_width => { - Cast::Unary(spirv::Op::FConvert) + Cast::Unary(spirv::Op::FConvert, expr_id) } - (Sk::Sint, Sk::Float, Some(_)) => Cast::Unary(spirv::Op::ConvertSToF), + (Sk::Sint, Sk::Float, Some(_)) => Cast::Unary(spirv::Op::ConvertSToF, expr_id), (Sk::Sint, Sk::Sint, Some(dst_width)) if src_scalar.width != dst_width => { - Cast::Unary(spirv::Op::SConvert) + Cast::Unary(spirv::Op::SConvert, expr_id) } - (Sk::Uint, Sk::Float, Some(_)) => Cast::Unary(spirv::Op::ConvertUToF), + (Sk::Uint, Sk::Float, Some(_)) => Cast::Unary(spirv::Op::ConvertUToF, expr_id), (Sk::Uint, Sk::Uint, Some(dst_width)) if src_scalar.width != dst_width => { - Cast::Unary(spirv::Op::UConvert) + Cast::Unary(spirv::Op::UConvert, expr_id) } (Sk::Uint, Sk::Sint, Some(dst_width)) if src_scalar.width != dst_width => { - Cast::Unary(spirv::Op::SConvert) + Cast::Unary(spirv::Op::SConvert, expr_id) } (Sk::Sint, Sk::Uint, Some(dst_width)) if src_scalar.width != dst_width => { - Cast::Unary(spirv::Op::UConvert) + Cast::Unary(spirv::Op::UConvert, expr_id) } // We assume it's either an identity cast, or int-uint. - _ => Cast::Unary(spirv::Op::Bitcast), + _ => Cast::Unary(spirv::Op::Bitcast, expr_id), }; Ok(match cast { - Cast::Identity => expr_id, - Cast::Unary(op) => { + Cast::Identity(expr) => expr, + Cast::Unary(op, op1) => { let id = self.gen_id(); block .body - .push(Instruction::unary(op, result_type_id, id, expr_id)); + .push(Instruction::unary(op, result_type_id, id, op1)); id } - Cast::Binary(op, operand) => { + Cast::Binary(op, op1, op2) => { let id = self.gen_id(); - block.body.push(Instruction::binary( - op, - result_type_id, - id, - expr_id, - operand, - )); + block + .body + .push(Instruction::binary(op, result_type_id, id, op1, op2)); id } - Cast::Ternary(op, op1, op2) => { + Cast::Ternary(op, op1, op2, op3) => { let id = self.gen_id(); - block.body.push(Instruction::ternary( - op, - result_type_id, - id, - expr_id, - op1, - op2, - )); + block + .body + .push(Instruction::ternary(op, result_type_id, id, op1, op2, op3)); id } }) diff --git a/third_party/rust/naga/src/back/wgsl/writer.rs b/third_party/rust/naga/src/back/wgsl/writer.rs index 62ad3aa644e2..9ca746e64f92 100644 --- a/third_party/rust/naga/src/back/wgsl/writer.rs +++ b/third_party/rust/naga/src/back/wgsl/writer.rs @@ -1147,11 +1147,12 @@ impl Writer { crate::Literal::Bool(value) => write!(self.out, "{value}")?, crate::Literal::F64(value) => write!(self.out, "{value:?}lf")?, crate::Literal::I64(value) => { - // `-9223372036854775808li` is not valid WGSL. The most negative `i64` - // value can only be expressed in WGSL using AbstractInt and - // a unary negation operator. + // `-9223372036854775808li` is not valid WGSL. Nor can we use the AbstractInt + // trick above, as AbstractInt also cannot represent `9223372036854775808`. + // The most negative `i64` value can only be expressed in WGSL using + // subtracting 1 from the second most negative value. if value == i64::MIN { - write!(self.out, "i64({value})")?; + write!(self.out, "{}li - 1li", value + 1)?; } else { write!(self.out, "{value}li")?; } diff --git a/third_party/rust/naga/src/common/wgsl/to_wgsl.rs b/third_party/rust/naga/src/common/wgsl/to_wgsl.rs index 73dea711e74d..82fe7c592597 100644 --- a/third_party/rust/naga/src/common/wgsl/to_wgsl.rs +++ b/third_party/rust/naga/src/common/wgsl/to_wgsl.rs @@ -21,7 +21,7 @@ pub trait ToWgsl: Sized { } /// Types that may be able to return the WGSL source representation -/// for their values as a `'static' string. +/// for their values as a `'static` string. /// /// This trait is specifically for types whose values are either /// simple enough that their WGSL form can be represented a static diff --git a/third_party/rust/naga/src/error.rs b/third_party/rust/naga/src/error.rs index dbc99651ff9a..81aa99d9d7e5 100644 --- a/third_party/rust/naga/src/error.rs +++ b/third_party/rust/naga/src/error.rs @@ -40,7 +40,7 @@ impl fmt::Display for ShaderError let label = self.label.as_deref().unwrap_or_default(); let files = SimpleFile::new(label, &self.source); let config = term::Config::default(); - let mut writer = termcolor::NoColor::new(Vec::new()); + let mut writer = term::termcolor::NoColor::new(Vec::new()); term::emit(&mut writer, &config, &files, &self.inner.diagnostic()) .expect("cannot write error"); write!( diff --git a/third_party/rust/naga/src/front/glsl/context.rs b/third_party/rust/naga/src/front/glsl/context.rs index 5296c4f28c0f..e6c5546fb957 100644 --- a/third_party/rust/naga/src/front/glsl/context.rs +++ b/third_party/rust/naga/src/front/glsl/context.rs @@ -1258,7 +1258,7 @@ impl<'a> Context<'a> { right = self.add_expression( Expression::Compose { ty, - components: core::iter::repeat(right).take(cols as usize).collect(), + components: core::iter::repeat_n(right, cols as usize).collect(), }, meta, )?; diff --git a/third_party/rust/naga/src/front/glsl/error.rs b/third_party/rust/naga/src/front/glsl/error.rs index 966c97e51ed3..13c6790191ee 100644 --- a/third_party/rust/naga/src/front/glsl/error.rs +++ b/third_party/rust/naga/src/front/glsl/error.rs @@ -8,8 +8,8 @@ use alloc::{ use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::files::SimpleFile; use codespan_reporting::term; +use codespan_reporting::term::termcolor::{NoColor, WriteColor}; use pp_rs::token::PreprocessorError; -use termcolor::{NoColor, WriteColor}; use thiserror::Error; use super::token::TokenValue; diff --git a/third_party/rust/naga/src/front/glsl/functions.rs b/third_party/rust/naga/src/front/glsl/functions.rs index e5c8a43f631d..4d6e64162313 100644 --- a/third_party/rust/naga/src/front/glsl/functions.rs +++ b/third_party/rust/naga/src/front/glsl/functions.rs @@ -348,7 +348,7 @@ impl Frontend { } } _ => { - components = iter::repeat(value).take(columns as usize).collect(); + components = iter::repeat_n(value, columns as usize).collect(); } } diff --git a/third_party/rust/naga/src/front/spv/error.rs b/third_party/rust/naga/src/front/spv/error.rs index 42b1bca086c4..016c690c4354 100644 --- a/third_party/rust/naga/src/front/spv/error.rs +++ b/third_party/rust/naga/src/front/spv/error.rs @@ -7,7 +7,7 @@ use alloc::{ use codespan_reporting::diagnostic::Diagnostic; use codespan_reporting::files::SimpleFile; use codespan_reporting::term; -use termcolor::{NoColor, WriteColor}; +use codespan_reporting::term::termcolor::{NoColor, WriteColor}; use super::ModuleState; use crate::{arena::Handle, front::atomic_upgrade}; diff --git a/third_party/rust/naga/src/front/spv/mod.rs b/third_party/rust/naga/src/front/spv/mod.rs index ca40b8efd0c5..b62f00d286c3 100644 --- a/third_party/rust/naga/src/front/spv/mod.rs +++ b/third_party/rust/naga/src/front/spv/mod.rs @@ -3040,7 +3040,6 @@ impl> Frontend { } Op::FunctionCall => { inst.expect_at_least(4)?; - block.extend(emitter.finish(ctx.expressions)); let result_type_id = self.next()?; let result_id = self.next()?; @@ -3053,6 +3052,8 @@ impl> Frontend { arguments.push(get_expr_handle!(arg_id, lexp)); } + block.extend(emitter.finish(ctx.expressions)); + // We just need an unique handle here, nothing more. let function = self.add_call(ctx.function_id, func_id); diff --git a/third_party/rust/naga/src/front/wgsl/error.rs b/third_party/rust/naga/src/front/wgsl/error.rs index 346cd8e6ce14..4a1f3a30a02f 100644 --- a/third_party/rust/naga/src/front/wgsl/error.rs +++ b/third_party/rust/naga/src/front/wgsl/error.rs @@ -14,7 +14,7 @@ use super::parse::lexer::Token; use codespan_reporting::diagnostic::{Diagnostic, Label}; use codespan_reporting::files::SimpleFile; use codespan_reporting::term; -use termcolor::{ColorChoice, NoColor, StandardStream}; +use codespan_reporting::term::termcolor::{ColorChoice, NoColor, StandardStream}; use thiserror::Error; use alloc::{ @@ -215,6 +215,7 @@ pub(crate) enum Error<'a> { }, DeclMissingTypeAndInit(Span), MissingAttribute(&'static str, Span), + InvalidAddrOfOperand(Span), InvalidAtomicPointer(Span), InvalidAtomicOperandType(Span), InvalidRayQueryPointer(Span), @@ -676,6 +677,11 @@ impl<'a> Error<'a> { )], notes: vec![], }, + Error::InvalidAddrOfOperand(span) => ParseError { + message: "cannot take the address of a vector component".to_string(), + labels: vec![(span, "invalid operand for address-of".into())], + notes: vec![], + }, Error::InvalidAtomicPointer(span) => ParseError { message: "atomic operation is done on a pointer to a non-atomic".to_string(), labels: vec![(span, "atomic pointer is invalid".into())], diff --git a/third_party/rust/naga/src/front/wgsl/lower/mod.rs b/third_party/rust/naga/src/front/wgsl/lower/mod.rs index d63443739e37..1fa3ecc83f03 100644 --- a/third_party/rust/naga/src/front/wgsl/lower/mod.rs +++ b/third_party/rust/naga/src/front/wgsl/lower/mod.rs @@ -2077,6 +2077,21 @@ impl<'source, 'temp> Lowerer<'source, 'temp> { // reference is required, the Load Rule is not applied. match self.expression_for_reference(expr, ctx)? { Typed::Reference(handle) => { + let expr = &ctx.runtime_expression_ctx(span)?.function.expressions[handle]; + if let &crate::Expression::Access { base, .. } + | &crate::Expression::AccessIndex { base, .. } = expr + { + if let Some(ty) = resolve_inner!(ctx, base).pointer_base_type() { + if matches!( + *ty.inner_with(&ctx.module.types), + crate::TypeInner::Vector { .. }, + ) { + return Err(Box::new(Error::InvalidAddrOfOperand( + ctx.get_expression_span(handle), + ))); + } + } + } // No code is generated. We just declare the reference a pointer now. return Ok(Typed::Plain(handle)); } @@ -2149,30 +2164,13 @@ impl<'source, 'temp> Lowerer<'source, 'temp> { } } - let temp_inner; + let temp_ty; let composite_type: &crate::TypeInner = match lowered_base { Typed::Reference(handle) => { - let inner = resolve_inner!(ctx, handle); - match *inner { - crate::TypeInner::Pointer { base, .. } => &ctx.module.types[base].inner, - crate::TypeInner::ValuePointer { - size: None, scalar, .. - } => { - temp_inner = crate::TypeInner::Scalar(scalar); - &temp_inner - } - crate::TypeInner::ValuePointer { - size: Some(size), - scalar, - .. - } => { - temp_inner = crate::TypeInner::Vector { size, scalar }; - &temp_inner - } - _ => unreachable!( - "In Typed::Reference(handle), handle must be a Naga pointer" - ), - } + temp_ty = resolve_inner!(ctx, handle) + .pointer_base_type() + .expect("In Typed::Reference(handle), handle must be a Naga pointer"); + temp_ty.inner_with(&ctx.module.types) } Typed::Plain(handle) => { diff --git a/third_party/rust/naga/src/front/wgsl/parse/mod.rs b/third_party/rust/naga/src/front/wgsl/parse/mod.rs index fe878969b577..af6c5ac09d18 100644 --- a/third_party/rust/naga/src/front/wgsl/parse/mod.rs +++ b/third_party/rust/naga/src/front/wgsl/parse/mod.rs @@ -3120,12 +3120,7 @@ impl Parser { // > … // > // > Maximum nesting depth of brace-enclosed statements in a function[:] 127 - // - // _However_, we choose 64 instead because (a) it avoids stack overflows in CI and - // (b) we expect the limit to be decreased to 63 based on this conversation in - // WebGPU CTS upstream: - // - const BRACE_NESTING_MAXIMUM: u8 = 64; + const BRACE_NESTING_MAXIMUM: u8 = 127; if brace_nesting_level + 1 > BRACE_NESTING_MAXIMUM { return Err(Box::new(Error::ExceededLimitForNestedBraces { span: brace_span, diff --git a/third_party/rust/naga/src/ir/block.rs b/third_party/rust/naga/src/ir/block.rs index fde2961c89ff..a842a2f3cc7b 100644 --- a/third_party/rust/naga/src/ir/block.rs +++ b/third_party/rust/naga/src/ir/block.rs @@ -23,9 +23,7 @@ impl Block { } pub fn from_vec(body: Vec) -> Self { - let span_info = core::iter::repeat(Span::default()) - .take(body.len()) - .collect(); + let span_info = core::iter::repeat_n(Span::default(), body.len()).collect(); Self { body, span_info } } diff --git a/third_party/rust/naga/src/ir/mod.rs b/third_party/rust/naga/src/ir/mod.rs index 167871a8c0ec..fa7bf63790e9 100644 --- a/third_party/rust/naga/src/ir/mod.rs +++ b/third_party/rust/naga/src/ir/mod.rs @@ -979,12 +979,12 @@ pub enum UnaryOperator { /// either on the left or the right. /// /// - A [`Matrix`] on the left can be multiplied by a [`Vector`] on the right -/// if the matrix has as many columns as the vector has components (`matCxR -/// * VecC`). +/// if the matrix has as many columns as the vector has components +/// (`matCxR * VecC`). /// /// - A [`Vector`] on the left can be multiplied by a [`Matrix`] on the right -/// if the matrix has as many rows as the vector has components (`VecR * -/// matCxR`). +/// if the matrix has as many rows as the vector has components +/// (`VecR * matCxR`). /// /// - Two matrices can be multiplied if the left operand has as many columns /// as the right operand has rows (`matNxR * matCxN`). @@ -1405,12 +1405,7 @@ pub enum Expression { /// Reference a function parameter, by its index. /// - /// A `FunctionArgument` expression evaluates to a pointer to the argument's - /// value. You must use a [`Load`] expression to retrieve its value, or a - /// [`Store`] statement to assign it a new value. - /// - /// [`Load`]: Expression::Load - /// [`Store`]: Statement::Store + /// A `FunctionArgument` expression evaluates to the argument's value. FunctionArgument(u32), /// Reference a global variable. @@ -2263,7 +2258,7 @@ pub struct SpecialTypes { /// this if needed and return the handle. pub ray_intersection: Option>, - /// Type for `RayVertexReturn + /// Type for `RayVertexReturn`. /// /// Call [`Module::generate_vertex_return_type`] pub ray_vertex_return: Option>, diff --git a/third_party/rust/naga/src/non_max_u32.rs b/third_party/rust/naga/src/non_max_u32.rs index da866f738084..530ebb450c29 100644 --- a/third_party/rust/naga/src/non_max_u32.rs +++ b/third_party/rust/naga/src/non_max_u32.rs @@ -42,11 +42,11 @@ use core::num::NonZeroU32; /// /// Although this should not be observable to its users, a `NonMaxU32` whose /// value is `n` is a newtype around a [`NonZeroU32`] whose value is `n + 1`. -/// This way, the range of values that `NonMaxU32` can represent, `0..=u32::MAX -/// - 1`, is mapped to the range `1..=u32::MAX`, which is the range that -/// [`NonZeroU32`] can represent. (And conversely, since [`u32`] addition wraps -/// around, the value unrepresentable in `NonMaxU32`, [`u32::MAX`], becomes the -/// value unrepresentable in [`NonZeroU32`], `0`.) +/// This way, the range of values that `NonMaxU32` can represent, +/// `0..=u32::MAX - 1`, is mapped to the range `1..=u32::MAX`, which is the +/// range that /// [`NonZeroU32`] can represent. (And conversely, since +/// [`u32`] addition wraps around, the value unrepresentable in `NonMaxU32`, +/// [`u32::MAX`], becomes the value unrepresentable in [`NonZeroU32`], `0`.) /// /// [`NonZeroU32`]: core::num::NonZeroU32 #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] diff --git a/third_party/rust/naga/src/proc/constant_evaluator.rs b/third_party/rust/naga/src/proc/constant_evaluator.rs index b6254a3e10c5..406dbbef52d3 100644 --- a/third_party/rust/naga/src/proc/constant_evaluator.rs +++ b/third_party/rust/naga/src/proc/constant_evaluator.rs @@ -474,7 +474,6 @@ impl ExpressionKindTracker { fun, Mf::Dot | Mf::Outer - | Mf::Cross | Mf::Distance | Mf::Length | Mf::Normalize @@ -565,6 +564,10 @@ pub enum ConstantEvaluatorError { InvalidRelationalArg(RelationalFunction), #[error("value of `low` is greater than `high` for clamp built-in function")] InvalidClamp, + #[error("Constructor expects {expected} components, found {actual}")] + InvalidVectorComposeLength { expected: usize, actual: usize }, + #[error("Constructor must only contain vector or scalar arguments")] + InvalidVectorComposeComponent, #[error("Splat is defined only on scalar values")] SplatScalarOnly, #[error("Can only swizzle vector constants")] @@ -586,8 +589,6 @@ pub enum ConstantEvaluatorError { value: String, to_type: &'static str, }, - #[error("abstract floating-point values cannot be automatically converted to integers")] - AutomaticConversionFloatToInt { to_type: &'static str }, #[error("Division by zero")] DivisionByZero, #[error("Remainder by zero")] @@ -1345,12 +1346,116 @@ impl<'a> ConstantEvaluator<'a> { component_wise_concrete_int(self, span, [arg], |ci| Ok(first_leading_bit(ci))) } + // vector + crate::MathFunction::Cross => self.cross_product(arg, arg1.unwrap(), span), + fun => Err(ConstantEvaluatorError::NotImplemented(format!( "{fun:?} built-in function" ))), } } + /// Vector cross product. + fn cross_product( + &mut self, + a: Handle, + b: Handle, + span: Span, + ) -> Result, ConstantEvaluatorError> { + use Literal as Li; + + let (a, ty) = self.extract_vec::<3>(a)?; + let (b, _) = self.extract_vec::<3>(b)?; + + let product = match (a, b) { + ( + [Li::AbstractInt(a0), Li::AbstractInt(a1), Li::AbstractInt(a2)], + [Li::AbstractInt(b0), Li::AbstractInt(b1), Li::AbstractInt(b2)], + ) => { + // `cross` has no overload for AbstractInt, so AbstractInt + // arguments are automatically converted to AbstractFloat. Since + // `f64` has a much wider range than `i64`, there's no danger of + // overflow here. + let p = cross_product( + [a0 as f64, a1 as f64, a2 as f64], + [b0 as f64, b1 as f64, b2 as f64], + ); + [ + Li::AbstractFloat(p[0]), + Li::AbstractFloat(p[1]), + Li::AbstractFloat(p[2]), + ] + } + ( + [Li::AbstractFloat(a0), Li::AbstractFloat(a1), Li::AbstractFloat(a2)], + [Li::AbstractFloat(b0), Li::AbstractFloat(b1), Li::AbstractFloat(b2)], + ) => { + let p = cross_product([a0, a1, a2], [b0, b1, b2]); + [ + Li::AbstractFloat(p[0]), + Li::AbstractFloat(p[1]), + Li::AbstractFloat(p[2]), + ] + } + ([Li::F16(a0), Li::F16(a1), Li::F16(a2)], [Li::F16(b0), Li::F16(b1), Li::F16(b2)]) => { + let p = cross_product([a0, a1, a2], [b0, b1, b2]); + [Li::F16(p[0]), Li::F16(p[1]), Li::F16(p[2])] + } + ([Li::F32(a0), Li::F32(a1), Li::F32(a2)], [Li::F32(b0), Li::F32(b1), Li::F32(b2)]) => { + let p = cross_product([a0, a1, a2], [b0, b1, b2]); + [Li::F32(p[0]), Li::F32(p[1]), Li::F32(p[2])] + } + ([Li::F64(a0), Li::F64(a1), Li::F64(a2)], [Li::F64(b0), Li::F64(b1), Li::F64(b2)]) => { + let p = cross_product([a0, a1, a2], [b0, b1, b2]); + [Li::F64(p[0]), Li::F64(p[1]), Li::F64(p[2])] + } + _ => return Err(ConstantEvaluatorError::InvalidMathArg), + }; + + let p0 = self.register_evaluated_expr(Expression::Literal(product[0]), span)?; + let p1 = self.register_evaluated_expr(Expression::Literal(product[1]), span)?; + let p2 = self.register_evaluated_expr(Expression::Literal(product[2]), span)?; + + self.register_evaluated_expr( + Expression::Compose { + ty, + components: vec![p0, p1, p2], + }, + span, + ) + } + + /// Extract the values of a `vecN` from `expr`. + /// + /// Return the value of `expr`, whose type is `vecN` for some + /// vector size `N` and scalar `S`, as an array of `N` [`Literal`] + /// values. + /// + /// Also return the type handle from the `Compose` expression. + fn extract_vec( + &mut self, + expr: Handle, + ) -> Result<([Literal; N], Handle), ConstantEvaluatorError> { + let span = self.expressions.get_span(expr); + let expr = self.eval_zero_value_and_splat(expr, span)?; + let Expression::Compose { ty, ref components } = self.expressions[expr] else { + return Err(ConstantEvaluatorError::InvalidMathArg); + }; + + let mut value = [Literal::Bool(false); N]; + for (component, elt) in + crate::proc::flatten_compose(ty, components, self.expressions, self.types) + .zip(value.iter_mut()) + { + let Expression::Literal(literal) = self.expressions[component] else { + return Err(ConstantEvaluatorError::InvalidMathArg); + }; + *elt = literal; + } + + Ok((value, ty)) + } + fn array_length( &mut self, array: Handle, @@ -1592,13 +1697,15 @@ impl<'a> ConstantEvaluator<'a> { Err(ConstantEvaluatorError::InvalidCastArg { from, to }) }; + use crate::proc::type_methods::IntFloatLimits; + let expr = match self.expressions[expr] { Expression::Literal(literal) => { let literal = match target { Sc::I32 => Literal::I32(match literal { Literal::I32(v) => v, Literal::U32(v) => v as i32, - Literal::F32(v) => v as i32, + Literal::F32(v) => v.clamp(i32::min_float(), i32::max_float()) as i32, Literal::F16(v) => f16::to_i32(&v).unwrap(), //Only None on NaN or Inf Literal::Bool(v) => v as i32, Literal::F64(_) | Literal::I64(_) | Literal::U64(_) => { @@ -1610,8 +1717,9 @@ impl<'a> ConstantEvaluator<'a> { Sc::U32 => Literal::U32(match literal { Literal::I32(v) => v as u32, Literal::U32(v) => v, - Literal::F32(v) => v as u32, - Literal::F16(v) => f16::to_u32(&v).unwrap(), //Only None on NaN or Inf + Literal::F32(v) => v.clamp(u32::min_float(), u32::max_float()) as u32, + // max(0) avoids None due to negative, therefore only None on NaN or Inf + Literal::F16(v) => f16::to_u32(&v.max(f16::ZERO)).unwrap(), Literal::Bool(v) => v as u32, Literal::F64(_) | Literal::I64(_) | Literal::U64(_) => { return make_error(); @@ -1622,9 +1730,9 @@ impl<'a> ConstantEvaluator<'a> { Sc::I64 => Literal::I64(match literal { Literal::I32(v) => v as i64, Literal::U32(v) => v as i64, - Literal::F32(v) => v as i64, + Literal::F32(v) => v.clamp(i64::min_float(), i64::max_float()) as i64, Literal::Bool(v) => v as i64, - Literal::F64(v) => v as i64, + Literal::F64(v) => v.clamp(i64::min_float(), i64::max_float()) as i64, Literal::I64(v) => v, Literal::U64(v) => v as i64, Literal::F16(v) => f16::to_i64(&v).unwrap(), //Only None on NaN or Inf @@ -1634,12 +1742,13 @@ impl<'a> ConstantEvaluator<'a> { Sc::U64 => Literal::U64(match literal { Literal::I32(v) => v as u64, Literal::U32(v) => v as u64, - Literal::F32(v) => v as u64, + Literal::F32(v) => v.clamp(u64::min_float(), u64::max_float()) as u64, Literal::Bool(v) => v as u64, - Literal::F64(v) => v as u64, + Literal::F64(v) => v.clamp(u64::min_float(), u64::max_float()) as u64, Literal::I64(v) => v as u64, Literal::U64(v) => v, - Literal::F16(v) => f16::to_u64(&v).unwrap(), //Only None on NaN or Inf + // max(0) avoids None due to negative, therefore only None on NaN or Inf + Literal::F16(v) => f16::to_u64(&v.max(f16::ZERO)).unwrap(), Literal::AbstractInt(v) => u64::try_from_abstract(v)?, Literal::AbstractFloat(v) => u64::try_from_abstract(v)?, }), @@ -1684,11 +1793,9 @@ impl<'a> ConstantEvaluator<'a> { Literal::F32(v) => v != 0.0, Literal::F16(v) => v != f16::zero(), Literal::Bool(v) => v, - Literal::F64(_) - | Literal::I64(_) - | Literal::U64(_) - | Literal::AbstractInt(_) - | Literal::AbstractFloat(_) => { + Literal::AbstractInt(v) => v != 0, + Literal::AbstractFloat(v) => v != 0.0, + Literal::F64(_) | Literal::I64(_) | Literal::U64(_) => { return make_error(); } }), @@ -1840,6 +1947,7 @@ impl<'a> ConstantEvaluator<'a> { Literal::I64(v) => Literal::I64(v.wrapping_neg()), Literal::F32(v) => Literal::F32(-v), Literal::F16(v) => Literal::F16(-v), + Literal::F64(v) => Literal::F64(-v), Literal::AbstractInt(v) => Literal::AbstractInt(v.wrapping_neg()), Literal::AbstractFloat(v) => Literal::AbstractFloat(-v), _ => return Err(ConstantEvaluatorError::InvalidUnaryOpArg), @@ -2244,18 +2352,55 @@ impl<'a> ConstantEvaluator<'a> { } } + /// Returns the total number of components, after flattening, of a vector compose expression. + fn vector_compose_flattened_size( + &self, + components: &[Handle], + ) -> Result { + components + .iter() + .try_fold(0, |acc, c| -> Result<_, ConstantEvaluatorError> { + let size = match *self.resolve_type(*c)?.inner_with(self.types) { + TypeInner::Scalar(_) => 1, + // We trust that the vector size of `component` is correct, + // as it will have already been validated when `component` + // was registered. + TypeInner::Vector { size, .. } => size as usize, + _ => return Err(ConstantEvaluatorError::InvalidVectorComposeComponent), + }; + Ok(acc + size) + }) + } + fn register_evaluated_expr( &mut self, expr: Expression, span: Span, ) -> Result, ConstantEvaluatorError> { - // It suffices to only check literals, since we only register one - // expression at a time, `Compose` expressions can only refer to other - // expressions, and `ZeroValue` expressions are always okay. + // It suffices to only check_literal_value() for `Literal` expressions, + // since we only register one expression at a time, `Compose` + // expressions can only refer to other expressions, and `ZeroValue` + // expressions are always okay. if let Expression::Literal(literal) = expr { crate::valid::check_literal_value(literal)?; } + // Ensure vector composes contain the correct number of components. We + // do so here when each compose is registered to avoid having to deal + // with the mess each time the compose is used in another expression. + if let Expression::Compose { ty, ref components } = expr { + if let TypeInner::Vector { size, scalar: _ } = self.types[ty].inner { + let expected = size as usize; + let actual = self.vector_compose_flattened_size(components)?; + if expected != actual { + return Err(ConstantEvaluatorError::InvalidVectorComposeLength { + expected, + actual, + }); + } + } + } + Ok(self.append_expr(expr, span, ExpressionKind::Const)) } @@ -2492,13 +2637,19 @@ trait TryFromAbstract: Sized { /// WGSL, we follow WGSL's conversion rules here: /// /// - WGSL §6.1.2. Conversion Rank says that automatic conversions - /// to integers are either lossless or an error. + /// from `AbstractInt` to an integer type are either lossless or an + /// error. /// - /// - WGSL §14.6.4 Floating Point Conversion says that conversions + /// - WGSL §15.7.6 Floating Point Conversion says that conversions /// to floating point in constant expressions and override /// expressions are errors if the value is out of range for the /// destination type, but rounding is okay. /// + /// - WGSL §17.1.2 i32()/u32() constructors treat AbstractFloat as any + /// other floating point type, following the scalar floating point to + /// integral conversion algorithm (§15.7.6). There is no automatic + /// conversion from AbstractFloat to integer types. + /// /// [`AbstractInt`]: crate::Literal::AbstractInt /// [`Float`]: crate::Literal::Float fn try_from_abstract(value: T) -> Result; @@ -2577,26 +2728,46 @@ impl TryFromAbstract for f64 { } impl TryFromAbstract for i32 { - fn try_from_abstract(_: f64) -> Result { - Err(ConstantEvaluatorError::AutomaticConversionFloatToInt { to_type: "i32" }) + fn try_from_abstract(value: f64) -> Result { + // https://www.w3.org/TR/WGSL/#floating-point-conversion + // To convert a floating point scalar value X to an integer scalar type T: + // * If X is a NaN, the result is an indeterminate value in T. + // * If X is exactly representable in the target type T, then the + // result is that value. + // * Otherwise, the result is the value in T closest to truncate(X) and + // also exactly representable in the original floating point type. + // + // A rust cast satisfies these requirements apart from "the result + // is... exactly representable in the original floating point type". + // However, i32::MIN and i32::MAX are exactly representable by f64, so + // we're all good. + Ok(value as i32) } } impl TryFromAbstract for u32 { - fn try_from_abstract(_: f64) -> Result { - Err(ConstantEvaluatorError::AutomaticConversionFloatToInt { to_type: "u32" }) + fn try_from_abstract(value: f64) -> Result { + // As above, u32::MIN and u32::MAX are exactly representable by f64, + // so a simple rust cast is sufficient. + Ok(value as u32) } } impl TryFromAbstract for i64 { - fn try_from_abstract(_: f64) -> Result { - Err(ConstantEvaluatorError::AutomaticConversionFloatToInt { to_type: "i64" }) + fn try_from_abstract(value: f64) -> Result { + // As above, except we clamp to the minimum and maximum values + // representable by both f64 and i64. + use crate::proc::type_methods::IntFloatLimits; + Ok(value.clamp(i64::min_float(), i64::max_float()) as i64) } } impl TryFromAbstract for u64 { - fn try_from_abstract(_: f64) -> Result { - Err(ConstantEvaluatorError::AutomaticConversionFloatToInt { to_type: "u64" }) + fn try_from_abstract(value: f64) -> Result { + // As above, this time clamping to the minimum and maximum values + // representable by both f64 and u64. + use crate::proc::type_methods::IntFloatLimits; + Ok(value.clamp(u64::min_float(), u64::max_float()) as u64) } } @@ -2626,6 +2797,19 @@ impl TryFromAbstract for f16 { } } +fn cross_product(a: [T; 3], b: [T; 3]) -> [T; 3] +where + T: Copy, + T: core::ops::Mul, + T: core::ops::Sub, +{ + [ + a[1] * b[2] - a[2] * b[1], + a[2] * b[0] - a[0] * b[2], + a[0] * b[1] - a[1] * b[0], + ] +} + #[cfg(test)] mod tests { use alloc::{vec, vec::Vec}; diff --git a/third_party/rust/naga/src/proc/mod.rs b/third_party/rust/naga/src/proc/mod.rs index 3412f9cea48b..787a2f1a63c4 100644 --- a/third_party/rust/naga/src/proc/mod.rs +++ b/third_party/rust/naga/src/proc/mod.rs @@ -20,6 +20,7 @@ pub use layouter::{Alignment, LayoutError, LayoutErrorInner, Layouter, TypeLayou pub use namer::{EntryPointIndex, NameKey, Namer}; pub use terminator::ensure_block_returns; use thiserror::Error; +pub use type_methods::min_max_float_representable_by; pub use typifier::{ResolveContext, ResolveError, TypeResolution}; impl From for super::Scalar { @@ -590,7 +591,7 @@ pub fn flatten_compose<'arenas>( count = size as usize; } } - core::iter::repeat(expr).take(count) + core::iter::repeat_n(expr, count) } // Expressions like `vec4(vec3(vec2(6, 7), 8), 9)` require us to diff --git a/third_party/rust/naga/src/proc/type_methods.rs b/third_party/rust/naga/src/proc/type_methods.rs index d4089bc1fabb..07528c9963d7 100644 --- a/third_party/rust/naga/src/proc/type_methods.rs +++ b/third_party/rust/naga/src/proc/type_methods.rs @@ -128,6 +128,25 @@ impl crate::TypeInner { } } + /// If `self` is a pointer type, return its base type. + pub const fn pointer_base_type(&self) -> Option { + match *self { + crate::TypeInner::Pointer { base, .. } => Some(TypeResolution::Handle(base)), + crate::TypeInner::ValuePointer { + size: None, scalar, .. + } => Some(TypeResolution::Value(crate::TypeInner::Scalar(scalar))), + crate::TypeInner::ValuePointer { + size: Some(size), + scalar, + .. + } => Some(TypeResolution::Value(crate::TypeInner::Vector { + size, + scalar, + })), + _ => None, + } + } + pub fn is_atomic_pointer(&self, types: &crate::UniqueArena) -> bool { match *self { crate::TypeInner::Pointer { base, .. } => match types[base].inner { @@ -308,3 +327,114 @@ impl crate::TypeInner { } } } + +/// Helper trait for providing the min and max values exactly representable by +/// the integer type `Self` and floating point type `F`. +pub trait IntFloatLimits +where + F: num_traits::Float, +{ + /// Returns the minimum value exactly representable by the integer type + /// `Self` and floating point type `F`. + fn min_float() -> F; + /// Returns the maximum value exactly representable by the integer type + /// `Self` and floating point type `F`. + fn max_float() -> F; +} + +macro_rules! define_int_float_limits { + ($int:ty, $float:ty, $min:expr, $max:expr) => { + impl IntFloatLimits<$float> for $int { + fn min_float() -> $float { + $min + } + fn max_float() -> $float { + $max + } + } + }; +} + +define_int_float_limits!(i32, half::f16, half::f16::MIN, half::f16::MAX); +define_int_float_limits!(u32, half::f16, half::f16::ZERO, half::f16::MAX); +define_int_float_limits!(i64, half::f16, half::f16::MIN, half::f16::MAX); +define_int_float_limits!(u64, half::f16, half::f16::ZERO, half::f16::MAX); +define_int_float_limits!(i32, f32, -2147483648.0f32, 2147483520.0f32); +define_int_float_limits!(u32, f32, 0.0f32, 4294967040.0f32); +define_int_float_limits!( + i64, + f32, + -9223372036854775808.0f32, + 9223371487098961920.0f32 +); +define_int_float_limits!(u64, f32, 0.0f32, 18446742974197923840.0f32); +define_int_float_limits!(i32, f64, -2147483648.0f64, 2147483647.0f64); +define_int_float_limits!(u32, f64, 0.0f64, 4294967295.0f64); +define_int_float_limits!( + i64, + f64, + -9223372036854775808.0f64, + 9223372036854774784.0f64 +); +define_int_float_limits!(u64, f64, 0.0f64, 18446744073709549568.0f64); + +/// Returns a tuple of [`crate::Literal`]s representing the minimum and maximum +/// float values exactly representable by the provided float and integer types. +/// Panics if `float` is not one of `F16`, `F32`, or `F64`, or `int` is +/// not one of `I32`, `U32`, `I64`, or `U64`. +pub fn min_max_float_representable_by( + float: crate::Scalar, + int: crate::Scalar, +) -> (crate::Literal, crate::Literal) { + match (float, int) { + (crate::Scalar::F16, crate::Scalar::I32) => ( + crate::Literal::F16(i32::min_float()), + crate::Literal::F16(i32::max_float()), + ), + (crate::Scalar::F16, crate::Scalar::U32) => ( + crate::Literal::F16(u32::min_float()), + crate::Literal::F16(u32::max_float()), + ), + (crate::Scalar::F16, crate::Scalar::I64) => ( + crate::Literal::F16(i64::min_float()), + crate::Literal::F16(i64::max_float()), + ), + (crate::Scalar::F16, crate::Scalar::U64) => ( + crate::Literal::F16(u64::min_float()), + crate::Literal::F16(u64::max_float()), + ), + (crate::Scalar::F32, crate::Scalar::I32) => ( + crate::Literal::F32(i32::min_float()), + crate::Literal::F32(i32::max_float()), + ), + (crate::Scalar::F32, crate::Scalar::U32) => ( + crate::Literal::F32(u32::min_float()), + crate::Literal::F32(u32::max_float()), + ), + (crate::Scalar::F32, crate::Scalar::I64) => ( + crate::Literal::F32(i64::min_float()), + crate::Literal::F32(i64::max_float()), + ), + (crate::Scalar::F32, crate::Scalar::U64) => ( + crate::Literal::F32(u64::min_float()), + crate::Literal::F32(u64::max_float()), + ), + (crate::Scalar::F64, crate::Scalar::I32) => ( + crate::Literal::F64(i32::min_float()), + crate::Literal::F64(i32::max_float()), + ), + (crate::Scalar::F64, crate::Scalar::U32) => ( + crate::Literal::F64(u32::min_float()), + crate::Literal::F64(u32::max_float()), + ), + (crate::Scalar::F64, crate::Scalar::I64) => ( + crate::Literal::F64(i64::min_float()), + crate::Literal::F64(i64::max_float()), + ), + (crate::Scalar::F64, crate::Scalar::U64) => ( + crate::Literal::F64(u64::min_float()), + crate::Literal::F64(u64::max_float()), + ), + _ => unreachable!(), + } +} diff --git a/third_party/rust/naga/src/span.rs b/third_party/rust/naga/src/span.rs index fbc2de633d5b..02a4f5ffdbef 100644 --- a/third_party/rust/naga/src/span.rs +++ b/third_party/rust/naga/src/span.rs @@ -239,7 +239,7 @@ impl WithSpan { /// Return a [`SourceLocation`] for our first span, if we have one. pub fn location(&self, source: &str) -> Option { - if self.spans.is_empty() { + if self.spans.is_empty() || source.is_empty() { return None; } @@ -285,8 +285,8 @@ impl WithSpan { where E: Error, { + use codespan_reporting::term::termcolor::{ColorChoice, StandardStream}; use codespan_reporting::{files, term}; - use term::termcolor::{ColorChoice, StandardStream}; let files = files::SimpleFile::new(path, source); let config = term::Config::default(); @@ -308,8 +308,8 @@ impl WithSpan { where E: Error, { + use codespan_reporting::term::termcolor::NoColor; use codespan_reporting::{files, term}; - use term::termcolor::NoColor; let files = files::SimpleFile::new(path, source); let config = term::Config::default(); diff --git a/third_party/rust/naga/src/valid/expression.rs b/third_party/rust/naga/src/valid/expression.rs index 096bd4cae1bd..f5bbdef1abc9 100644 --- a/third_party/rust/naga/src/valid/expression.rs +++ b/third_party/rust/naga/src/valid/expression.rs @@ -698,8 +698,18 @@ impl super::Validator { let good = match query { crate::ImageQuery::NumLayers => arrayed, crate::ImageQuery::Size { level: None } => true, - crate::ImageQuery::Size { level: Some(_) } - | crate::ImageQuery::NumLevels => class.is_mipmapped(), + crate::ImageQuery::Size { level: Some(level) } => { + match resolver[level] { + Ti::Scalar(Sc::I32 | Sc::U32) => {} + _ => { + return Err(ExpressionError::InvalidImageOtherIndexType( + level, + )) + } + } + class.is_mipmapped() + } + crate::ImageQuery::NumLevels => class.is_mipmapped(), crate::ImageQuery::NumSamples => class.is_multisampled(), }; if !good { diff --git a/third_party/rust/naga/src/valid/function.rs b/third_party/rust/naga/src/valid/function.rs index d00a28e108b1..3697f015c9d2 100644 --- a/third_party/rust/naga/src/valid/function.rs +++ b/third_party/rust/naga/src/valid/function.rs @@ -7,6 +7,7 @@ use super::{ }; use crate::arena::{Arena, UniqueArena}; use crate::arena::{Handle, HandleSet}; +use crate::proc::TypeResolution; use crate::span::WithSpan; use crate::span::{AddSpan as _, MapErrWithSpan as _}; @@ -299,6 +300,10 @@ impl<'a> BlockContext<'a> { fn resolve_pointer_type(&self, handle: Handle) -> &crate::TypeInner { self.info[handle].ty.inner_with(self.types) } + + fn inner_type<'t>(&'t self, ty: &'t TypeResolution) -> &'t crate::TypeInner { + ty.inner_with(self.types) + } } impl super::Validator { @@ -1039,23 +1044,15 @@ impl super::Validator { } let pointer_ty = context.resolve_pointer_type(pointer); - - let good = match *pointer_ty { - Ti::Pointer { base, space: _ } => match context.types[base].inner { - Ti::Atomic(scalar) => *value_ty == Ti::Scalar(scalar), - ref other => value_ty == other, - }, - Ti::ValuePointer { - size: Some(size), - scalar, - space: _, - } => *value_ty == Ti::Vector { size, scalar }, - Ti::ValuePointer { - size: None, - scalar, - space: _, - } => *value_ty == Ti::Scalar(scalar), - _ => false, + let good = match pointer_ty + .pointer_base_type() + .as_ref() + .map(|ty| context.inner_type(ty)) + { + // The Naga IR allows storing a scalar to an atomic. + Some(&Ti::Atomic(ref scalar)) => *value_ty == Ti::Scalar(*scalar), + Some(other) => *value_ty == *other, + None => false, }; if !good { return Err(FunctionError::InvalidStoreTypes { pointer, value } diff --git a/third_party/rust/naga/src/valid/mod.rs b/third_party/rust/naga/src/valid/mod.rs index 8f5501bf0213..1580e79644ff 100644 --- a/third_party/rust/naga/src/valid/mod.rs +++ b/third_party/rust/naga/src/valid/mod.rs @@ -566,6 +566,7 @@ impl Validator { crate::Scalar::BOOL | crate::Scalar::I32 | crate::Scalar::U32 + | crate::Scalar::F16 | crate::Scalar::F32 | crate::Scalar::F64, ) => {} diff --git a/third_party/rust/ron/.cargo-checksum.json b/third_party/rust/ron/.cargo-checksum.json index 5004a77fbd24..8fa2d029a20c 100644 --- a/third_party/rust/ron/.cargo-checksum.json +++ b/third_party/rust/ron/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"CHANGELOG.md":"bdd87c85a3688ec08650dbd953df54fe0b2e8caa9df134e6f0d27e85a36735ca","Cargo.lock":"8509283147e687d6a4395ba465e763ce99c958ccb1e91289dc71f13db9c56ada","Cargo.toml":"ba9098bc92f15da67dcdc3264663a741ff78cef33824346350ad31ea13be0c62","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"fa0598d520497731445a5bc89a2b3d47896d95568bd2be255b7bc43d771b9994","README.md":"eb0da6ac295ea48163cc58683ca18caa554303c730aef4ef0ea259dfc04f3f14","clippy.toml":"53dcbaa544dd4d34fefe8aef4d287720223385c47f7b27cc1d6983c72843a9ca","docs/extensions.md":"47cb367dbef73952065fe1c0b1186603b108dc70b5d8abbf3eff3b75c1024912","docs/grammar.md":"18b0c90828b4d7089f7adced614ab4910935a1f927446367a23a9f2ee31dd54c","examples/decode.rs":"b8e2db901f0a25070daa0294794da7c95f9a7d61934e9b5b1cbcb131ca70a7d7","examples/decode_file.rs":"113bec048bae541618fdacd22b55052852cdf6f1a93b1f32e02a59d53a718e8b","examples/encode.rs":"cfded2357414e2ea68580bf8f2333a62c1ae9d9fa70775c56a23dfe158e297be","examples/example.ron":"aebd2baff31a62c3aed1ba568b7a7c1165b197d526a1e2c9eb7adb211de8292e","examples/transcode.rs":"7a1e281684243b263d28f90f2836d6098239ae8ab2a0cb9d97ee0b7521ba960e","rustfmt.toml":"10f292ebd4c21ef67c36b6edb79eed7fada8f021c527948dfbeb3a54a5591e59","src/de/id.rs":"6bf18960a4cc99e7d2408a94d5d47e78105b3912290a33c799291b7b2ff994d1","src/de/mod.rs":"45d44b02f7440648b2e3169d3023ad68cd02b6192c3c8524c04aebf4b0677f2d","src/de/tag.rs":"527c3ea01886612c17440608daa11a3e1b3aa3b11b8c3f06f68d6ba1fc70ba27","src/de/tests.rs":"41b62078e6837db673454a709e438901c5b52f8baa77e6b16881a1bfc1247791","src/de/value.rs":"f86a4eba21b31f4f700acd035009d4c426d04969f4762391112100471c4eaabb","src/error.rs":"a63d644181f088275332b147a0a4c3d5d1cf03602a7e8a58250ce5ddcb0ef159","src/extensions.rs":"58ae01101553d1a0758bcdc652bfd5c2f5123f2807e18270c6f35a4e95d1f44c","src/lib.rs":"63af8224ca12b95eaa548266243eb6b3de456b02a166071ea2b7ade2695ad9f7","src/options.rs":"cc05dc580794a8766824e7e034ee98e796590ea605f4575a529f5ef5c757f8ee","src/parse.rs":"497f44abb50535d72b6adfb8ba4e6bbde9665c08aaebb49ff88af1e2d2165c5d","src/ser/mod.rs":"582969f19cf8f53ec48143d3c3cd8eacae4733703e28434009df5b0ba971bd66","src/ser/tests.rs":"3dca7c683c3ede146bf17138f6445c38ee6f37c5a8afe08d76bb2921688f3981","src/ser/value.rs":"0c099c6c3c73062048c989dc8f70c3e67cfd3989a286da79a45323830c4a277b","src/value.rs":"14a6d9a69676d6fdda8216336ec35af3f0a0b2d4bbdd1725e07d172fa4d6b780","tests/117_untagged_tuple_variant.rs":"1ce8d384d84ff21a28dafef0f0ea4be9e6cbf9bd7c2ed4aa37e8d820c7b594b8","tests/123_enum_representation.rs":"776c666a4d388504bde9f2e27838d6cb389fcb6d705dfa077a1de7cdea1f26d6","tests/129_indexmap.rs":"a55ed888407dce347035893afa6a5e60d3e2f92672287c93cf5e21bb19218a40","tests/147_empty_sets_serialisation.rs":"1782934f9b19d56460d3e94c78698c1220d38bca801d46dd8474bb76bc00a21c","tests/152_bitflags.rs":"7e2cd4a62799f4b435924ed0909d391c3a680893bbeb84ca46fc5232ffd69886","tests/203_error_positions.rs":"9c5654a7dbde482040dc11521dd7db0f71dc42e5782d1df9908911c42ce4ff7e","tests/207_adjacently_tagged_enum.rs":"f2ebdf4268eeb6be95fd249c10930e4ea8f4fd4e3452d1ae8799334db2a3b96e","tests/238_array.rs":"30873e63e475b48df88dc8cc169569b6495ee24b27c37838ae27bf834b8d7ee7","tests/240_array_pretty.rs":"5187f3d50aa94db60d6fb42162921e9d94c7573c61767ded898a6ca50929f18b","tests/250_variant_newtypes.rs":"c3b4ffdd33163299f47d14f78137fc1496d7ad133717b85b3be195846a8c2fc2","tests/256_comma_error.rs":"c2e2d7b37d4810e0eb12af2c47dfd47fa64adf14a350a745c575ff02caaf6d41","tests/289_enumerate_arrays.rs":"0e1e9bd624b756714c6d79b79c388972c4b443c331d08098d585a85cba519bb5","tests/301_struct_name_mismatch.rs":"b980a004edadc4829479e96f897290de25c34c4efb364e5a7d8bd695fdfb78c0","tests/307_stack_overflow.rs":"b8affab09acdb6594242e7142e2fc9f6cd0f3bf36337129dc42b11f8d72060f5","tests/322_escape_idents.rs":"4f8912aeb24655a378049919ffc8b270519849abcb34ef9d7c34af5be1ec72f3","tests/337_value_float_roundtrip.rs":"d4fc07c7fd8c588ae46f8c1fd24caa29e044bf593c8a6754dc3353ddd44157ac","tests/359_deserialize_seed.rs":"be8d0ce834f68496cc90d05b2a0688c105c51883318e3eb362ce7c646d4dc5fe","tests/367_implicit_some.rs":"e222c356e8af4e5e3bd7b936daafa2519630d83dfddc784e07599fe3bfd92797","tests/370_float_parsing.rs":"23bd0e59e5fa5b51d1575d6fe620b04773c555428ec461849e04028f1d22be69","tests/393_serde_errors.rs":"0f9471ea1f5117df6916d0100f3cdb1073c7e9e0ba2da28caa7dfe7e1c63ab13","tests/401_raw_identifier.rs":"d073a92dd49b712fc6385aafa9a4b2f53bcc1d9ef88942fbc206232dc9bc6c2b","tests/410_trailing_comma.rs":"c8755b2805f26f0d9b748f4277a8d8eea85227af06788ae533cf7b96a8b9e724","tests/423_de_borrowed_identifier.rs":"b3adbdf97d7082d7fea2b2649bb6d50e3e7b320049671dad6d803c003a15586f","tests/462_bytes.rs":"7394a118a1bd1336301289b6d93a390fb5a46b2fbd13bccf68d4a6fab400b58b","tests/big_struct.rs":"9c7b41233ae7d0c4fec0727aabd4cea1cd95844c85c0beb5e688b3c79eb43c14","tests/borrowed_str.rs":"24390ee6076ce3896b7cf1b2e202cb3a705c61129a4f62f6c39a2412ca68405f","tests/comments.rs":"d88ba142978b5536f6891d839c89c420196f8927fd059eb25c087b555f235109","tests/depth_limit.rs":"775f872038c127abeb2e4474df67ad483f82d23fdb535060da14e498d05119f8","tests/escape.rs":"b43702a042b7078e9007fd4c90bb9b2d770565178946e95438e92981ea2ffc2d","tests/extensions.rs":"4058b5da64c3f9591026790e044d19cf30c744a30cd3e9af79acd70f76ec0d40","tests/floats.rs":"367a22cca7d3a3ce6bdffc30df8712aae348ad29a1adbe9a47bc98e0a51b614d","tests/large_number.rs":"415ad0374b22553b0cf7926b6d7a64505f8b6021d46b6a93587024449f60b353","tests/min_max.rs":"4529513a4cf1e5c25f7697ba258fdbae258617cf8f3374080843aef048c2bde3","tests/non_identifier_identifier.rs":"ba759adf7e0e93a9989b3e08155d424868f6b060ff8d2d7ef8fdd45433497990","tests/numbers.rs":"d28656afd5123df5cec3b8fe01cda8f14b6b3065900b7adbf352776a164dd29b","tests/options.rs":"8ff7bbc62503a5f6675a89a10059ec48c437da1c57000f5310e0d4b09fb3d5e2","tests/preserve_sequence.rs":"ade33555951c08eceaf38ff62b0d043f7e6a426496527dc4b94ceadf2a99344b","tests/preserve_sequence_ex1.ron":"47bdf8da637b6e99701b5a4b8c100b4c9d45440c357aef8d368a406a05394ea9","tests/preserve_sequence_ex2.ron":"9ba759300324f8978469ce616f20eb0cfc134f0a8a1afff884fff5315b32e0d0","tests/roundtrip.rs":"4af0accc1129850537f2e04fcf9c9c1d4ecf25fdd05e79179de8fb7f8596a910","tests/struct_integers.rs":"10f06e4d0845c8319236122047e6bda2765082fdf93b8d9c3770c29fb941086d","tests/to_string_pretty.rs":"96b3aaba638f90b66a16693714139ef57ecd8daefd8f87b771ae2bf37b15e985","tests/unicode.rs":"ac3944bf448f8cd5986bad5e7c4eca60302230c13378b213415dafc1d3ae2428","tests/value.rs":"71b12b8ff43b5e27efeba30e9d303156410d38a1f59d8bf680e450d5c379fdf3"},"package":"b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"} \ No newline at end of file +{"files":{"CHANGELOG.md":"69a39656a3c8c30ffd5a2e9193493a3631e1d414ca242b46dc6888434d735716","Cargo.lock":"2aac2bf17f890b208b2774ddae9be249c49cdc843131c50b0287e7491c21a2b9","Cargo.toml":"e2b6993ef291ea4aa4330841dfb67bb91362a3ae71236652a92c3d60d0644b79","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"fa0598d520497731445a5bc89a2b3d47896d95568bd2be255b7bc43d771b9994","README.md":"91b5bc0315a5f19e39313a048fb89aa1a5eda716fb6123f887731cc085b712d5","clippy.toml":"873fc9b7842395d9c61c2265ea4ad391c1de35bb25fd94347f72b4c7fc528329","docs/extensions.md":"b01d8c32d6adf1ece115957bc63400873aad6106a0f5b80e107e57bada605e5b","docs/grammar.md":"3c1f8423e197d234782724f5bf2cde5d6ed03e77ce63ddda7241717846739f81","examples/base64.rs":"a157559fe9932a834c0c42b726fa483ca648a7ceaf762639746410e84d019901","examples/decode.rs":"b8e2db901f0a25070daa0294794da7c95f9a7d61934e9b5b1cbcb131ca70a7d7","examples/decode_file.rs":"92920fc94e22c2aa83f6864e68e16932425655139d21ed3bdca4e5b4e86428e1","examples/encode.rs":"cfded2357414e2ea68580bf8f2333a62c1ae9d9fa70775c56a23dfe158e297be","examples/example.ron":"aebd2baff31a62c3aed1ba568b7a7c1165b197d526a1e2c9eb7adb211de8292e","examples/transcode.rs":"7a1e281684243b263d28f90f2836d6098239ae8ab2a0cb9d97ee0b7521ba960e","rustfmt.toml":"10f292ebd4c21ef67c36b6edb79eed7fada8f021c527948dfbeb3a54a5591e59","src/de/id.rs":"ed5be2ef39ad3490dcdebf99bd692299a20dbc66f2a2b67b0db1eb6dab5fd68b","src/de/mod.rs":"08cd27fd8d3f9f755ff3f1031f8a22dcdab6a2a5baefadd63991d3b98af2b458","src/de/tag.rs":"aeabf1a8378f2f4f8dc51861f0a92eb3f7eaefe3ce7924317fb923654a109153","src/de/tests.rs":"5af19a3ff8dc2d29f6d8d8f6953227454f47a9c6497f360b60d9f40d5b2e44c2","src/de/value.rs":"8de9194406494579988b27359d31aad7896903379c975787f20ec08064833d7e","src/error.rs":"4f244e7a14c89c5977814cea1c05946c88b2db1f0036e4e0d6ead97def0cd3df","src/extensions.rs":"e37b7658ab1fb7adbb09e5c6be4a44f28ee2dae6bc90f3adb66ee68283b768ef","src/lib.rs":"2a30dadcdb6a1f53e539c0f6bc7fea405df8317f7eee5431fcae5330c55c670d","src/options.rs":"22eed959d26dd6a101a998c423234dd2d62e746ef1e156f741bbd1126fd5ca27","src/parse.rs":"a6e8944c74ef17d963c795e614d32014d1eda098965a599e1b4539312c522fb0","src/ser/mod.rs":"cd7eeff262566d36179faca7d4b2351aabc49553f7c953ea79d94241cbe68164","src/ser/path_meta.rs":"fb74b63bdb8ed570f071670e4ca68886ae1c25611a0cd68fdc540d652680b08c","src/ser/raw.rs":"ae5369904a4db31a36eb966204d96592fde92ab2dfb18165f010a9cf13b8b0dd","src/ser/tests.rs":"61aa2d6d18be8040b305e4962d0c80e5b402101f23204139f8e4a76e958c6c4d","src/ser/value.rs":"476f186322ba027219b0d01b5a1bcdaa33ccbeeb24ab062a477e6128816a6177","src/value/map.rs":"09de618c465e4291b59162ba61b1dc0f689f87910ba86b46fdc362396616c954","src/value/mod.rs":"c1fccd91db3024038d0e37f2925cbc2a831a5714bce8706290c4b6151253b1c8","src/value/number.rs":"2cde441d57ac43f21b12e3e8465830b0d01429e9727cb6a8af750cc8a7b9dae3","src/value/raw.rs":"22127a25e79750c0108f6b801665d5dc731683ad6538154b04ed9e2e5518c090","tests/115_minimal_flattening.rs":"3bc62fa93ddc6abf5ea504090cdf09b6fd65e1e2bff27c42454054c732f732f5","tests/117_untagged_tuple_variant.rs":"dc69fb057595cc72b379994e76217315761484a455c7ba45aceb445af9c36403","tests/123_enum_representation.rs":"c296885ed5dcd3a7b5230d639aa7632c1c6291460e73dd394b0cd9e69219de31","tests/129_indexmap.rs":"a55ed888407dce347035893afa6a5e60d3e2f92672287c93cf5e21bb19218a40","tests/147_empty_sets_serialisation.rs":"102daa5648f4aaa461d3dc79a9124ff57c92d6c361c2aa20a3f966a0c0908823","tests/152_bitflags.rs":"e93d135e4da6c591f697be7d3b4c116d9151ab52111fe39fda94926f384ad563","tests/203_error_positions.rs":"9c5654a7dbde482040dc11521dd7db0f71dc42e5782d1df9908911c42ce4ff7e","tests/207_adjacently_tagged_enum.rs":"f2ebdf4268eeb6be95fd249c10930e4ea8f4fd4e3452d1ae8799334db2a3b96e","tests/217_nested_untagged_enums.rs":"91f7f0d344b256c707091a8985a1a15e799796ed757901077e3fe5a230c4ecae","tests/238_array.rs":"b0cae4bbe6303da34a58f8c28484856eebccc1728e21847cc2651a30dfd7346d","tests/240_array_pretty.rs":"7cedc29d8bdd9bf40941f3e28a8f1200e72c5211d5e52376e5d5be9076286e36","tests/250_variant_newtypes.rs":"be8eb265ead0386319f17d9340c6897f6ee7945177e541874e60d00d63a9a9a1","tests/254_typetag.rs":"e20c1464eaf2db561444c27fbdf4d73bc6e312e0c57915b7611b06f74a0dc654","tests/256_comma_error.rs":"dd9dc3440c779b45cbcc9cdc39937d1b7045530cdea7255025aaf5a1f1fe023d","tests/289_enumerate_arrays.rs":"0e1e9bd624b756714c6d79b79c388972c4b443c331d08098d585a85cba519bb5","tests/301_struct_name_mismatch.rs":"b980a004edadc4829479e96f897290de25c34c4efb364e5a7d8bd695fdfb78c0","tests/307_stack_overflow.rs":"b8affab09acdb6594242e7142e2fc9f6cd0f3bf36337129dc42b11f8d72060f5","tests/321_unicode_ident.rs":"b32560400d21a633734b366c743a13d972afc12c1d73552485f93a397d8b349a","tests/322_escape_idents.rs":"4f8912aeb24655a378049919ffc8b270519849abcb34ef9d7c34af5be1ec72f3","tests/337_value_float_roundtrip.rs":"e1d2b0ab6ded31fae940b1a937166c385711e68c1f9c716aead7abbeaf17ea01","tests/357_untagged_enum_roundtrip.rs":"193e1804f79bb20b93a9a80203fa64952d811bda6676d808947ac33ec27c2a90","tests/359_deserialize_seed.rs":"be8d0ce834f68496cc90d05b2a0688c105c51883318e3eb362ce7c646d4dc5fe","tests/367_implicit_some.rs":"0c4ebdb13872079954b83ea1a89e0d1fb4714b406fd9dcac976754534e9e36d5","tests/370_float_parsing.rs":"ddb7c92cf6ec9cc5048c97d6199ecc6e32bb68daa5521f381cfad1b841eac106","tests/393_serde_errors.rs":"0f9471ea1f5117df6916d0100f3cdb1073c7e9e0ba2da28caa7dfe7e1c63ab13","tests/401_raw_identifier.rs":"7512950a2db5c527525c145c746297375780e5e6da7db900b1caf58b595e093f","tests/407_raw_value.rs":"7b3107498cb384ad9a8cf7803c521933e4571dc297fe2c78118f8280f69bb4f3","tests/410_trailing_comma.rs":"c8755b2805f26f0d9b748f4277a8d8eea85227af06788ae533cf7b96a8b9e724","tests/423_de_borrowed_identifier.rs":"b3adbdf97d7082d7fea2b2649bb6d50e3e7b320049671dad6d803c003a15586f","tests/425_escape_strings.rs":"d30a63591d5f517b33ac3aaac927a10e1f22d673083497847d0d08b243d2e6b5","tests/436_untagged_bytes.rs":"88fe49032243fd62b5b004c62b369cef25f0d38c4fc2679899f6f0a70b5ff303","tests/438_rusty_byte_strings.rs":"13833d174938d249c2e999eb4e1503d7744ca1504dfa397091127d25e87ab724","tests/447_compact_maps_structs.rs":"cff3aa89e123e8c8098e49e4b1c4f2725f94c8eb1d2ed2abd7cff46695e1df29","tests/449_tagged_enum.rs":"bf21e40e14ea1d39460396af510ca8a1378d7bee489e369fb3dcc3f2fc96f617","tests/462_bytes.rs":"b58b44538525a560c19bb84d64236b9fc61e6a09c117e5c4581f8fe39a8251a3","tests/465_implicit_some_stack.rs":"05621c22436766e7f38d675137aedeb94efbb2c15a761952701cd1a8fe6fb120","tests/465_no_comment_char_value.rs":"bf1b7aae4b4c5e982511e13cddc1fd2eca9c52173d747ce8f829c3e9d99ba55e","tests/465_r_name_value.rs":"17e87a47b5a22d0f068698d557bd46e5d64c3083b1906c8a0fede56529a06d19","tests/465_ser_backslash_string.rs":"871629c618ce378ae0ce27c49c44fd3b95add29b01fde02eb452fa11fc35a575","tests/465_unwrap_some_newtype_variant_value.rs":"6a8ffe1939894d7dedfeafbf73cfe19ca1c665beb892deaadd25bfe1fabb923c","tests/465_validate_ser_identifiers.rs":"84d407d71666312538a6ccd3b350ad025e6199a65c48364705dfd5a2ad16930a","tests/481_number_underscores_suffixes.rs":"16ebad0c50f88c8750bd770d405b88ea00914f210230f4aaeca2a873207084b8","tests/492_enum_in_untagged_enum.rs":"ecf9a5e190317ecdc5f4688b82de0266a6d15712f5d8f19599dff35efa3d4b15","tests/502_known_bugs.rs":"34d2d723c2eeecad0316a17056d7d48d7c4e48660065697f90110301bbcda945","tests/508_value_adjacently_tagged_bug.rs":"3460ecf769a04e73f8adcc97f6548657c337466c23cb69a111f892e10667a582","tests/511_deserialize_any_map_string_key.rs":"45cf9624ce56eb64616c951291a322abfdc65f41ced7105cc74b754a386ad35b","tests/522_explicit_struct_names.rs":"c1405f6e31157d042e7cb4ea61c15127bc15b5c785b3ca7121b0ec1f8ef6e24c","tests/526_flatten.rs":"96ad9a3934a83266b662c06cb6b71425999d8ff9751bed9250dd8a10def2f3b7","tests/530_untagged_union.rs":"13623c4dc2deb98bc05537daa0a557568314e98bba86c4059d60b0fc2db10354","tests/544_path_meta.rs":"3ad38d27ac802e2edc2268093503940a0ecb40d54d96cc91d49376b0e1807832","tests/big_struct.rs":"9c7b41233ae7d0c4fec0727aabd4cea1cd95844c85c0beb5e688b3c79eb43c14","tests/borrowed_str.rs":"24390ee6076ce3896b7cf1b2e202cb3a705c61129a4f62f6c39a2412ca68405f","tests/comments.rs":"251f623fb151b24d874c7c08118b22d4579fcfc8831c0c1c97ab11b8f0edeeb7","tests/depth_limit.rs":"817c0a61576c3133a66bb8c705675a125290f0ccfb92dba8a8aa46a141ad30ce","tests/escape.rs":"4ad32604687f67c64cf7f4757fdded9e1131fb4c65bd5daa8079b1b1c60db7b7","tests/extensions.rs":"4058b5da64c3f9591026790e044d19cf30c744a30cd3e9af79acd70f76ec0d40","tests/floats.rs":"367a22cca7d3a3ce6bdffc30df8712aae348ad29a1adbe9a47bc98e0a51b614d","tests/large_number.rs":"1f823b826e086f35329849d5bd9aac87266933a07988ef380348b406dea32835","tests/min_max.rs":"4529513a4cf1e5c25f7697ba258fdbae258617cf8f3374080843aef048c2bde3","tests/non_identifier_identifier.rs":"b00d2a4994242212f2e3f2c847da3a3634b950060d88b74a7004d704e847f2df","tests/non_string_tag.rs":"4ea293cb6110b3776a103000a50a7276f50acc5aa15aa65f651a2e27a441dbba","tests/numbers.rs":"d28656afd5123df5cec3b8fe01cda8f14b6b3065900b7adbf352776a164dd29b","tests/options.rs":"15d0ebd3ac481aa18b56bc40ef5e28a5e36577f7054632bb26334b0167f2872f","tests/preserve_sequence.rs":"cda4aa098e579cf68837101b8e6b12f5b26e11272cc17d260fa232379c5008bf","tests/preserve_sequence_ex1.ron":"47bdf8da637b6e99701b5a4b8c100b4c9d45440c357aef8d368a406a05394ea9","tests/preserve_sequence_ex2.ron":"9ba759300324f8978469ce616f20eb0cfc134f0a8a1afff884fff5315b32e0d0","tests/roundtrip.rs":"4af0accc1129850537f2e04fcf9c9c1d4ecf25fdd05e79179de8fb7f8596a910","tests/struct_integers.rs":"005ff6238fdd953b822fa0ad87c013370da646bd63f880a476a2d9bbe3da7b57","tests/to_string_pretty.rs":"96b3aaba638f90b66a16693714139ef57ecd8daefd8f87b771ae2bf37b15e985","tests/unicode.rs":"7bb3d2e06d7656fabd1f8940a5abd92af6ffa8652b2f2fa7b0f2b86ce009a3c6","tests/value.rs":"22c53deb9c7b64f6d34fd345dcf5fadcca7e2508714a696b967d42d464fe671b"},"package":"63f3aa105dea217ef30d89581b65a4d527a19afc95ef5750be3890e8d3c5b837"} \ No newline at end of file diff --git a/third_party/rust/ron/CHANGELOG.md b/third_party/rust/ron/CHANGELOG.md index fd9ec5977d06..a50ad8f3bc15 100644 --- a/third_party/rust/ron/CHANGELOG.md +++ b/third_party/rust/ron/CHANGELOG.md @@ -6,7 +6,56 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## Unreleased +## [0.9.0] - 2025-03-18 + +### API Changes + +- Add `ron::value::RawValue` helper type which can (de)serialize any valid RON ([#407](https://github.com/ron-rs/ron/pull/407)) +- Add `escape_strings` option to `PrettyConfig` to allow serialising with or without escaping ([#426](https://github.com/ron-rs/ron/pull/426)) +- Add `compact_maps` and `compact_structs` options to `PrettyConfig` to allow serialising maps and structs on a single line ([#448](https://github.com/ron-rs/ron/pull/448)) +- Add minimal support for `#[serde(flatten)]` with roundtripping through RON maps ([#455](https://github.com/ron-rs/ron/pull/455)) +- Add minimal roundtripping support for `#[serde(tag = "tag")]`, `#[serde(tag = "tag", content = "content")]`, and `#[serde(untagged)]` enums ([#451](https://github.com/ron-rs/ron/pull/451)) +- Breaking: Expand the `value::Number` enum to explicitly encode all possible number types ([#479](https://github.com/ron-rs/ron/pull/479)) +- Add `number_suffixes` option to `PrettyConfig` to allow serialising numbers with their explicit type suffix, e.g. `42i32` ([#481](https://github.com/ron-rs/ron/pull/481)) +- Allow `ron::value::RawValue` to capture any whitespace to the left and right of a ron value ([#487](https://github.com/ron-rs/ron/pull/487)) +- Breaking: Enforce that ron always writes valid UTF-8 ([#488](https://github.com/ron-rs/ron/pull/488)) +- Add convenient `Value::from` impls ([#498](https://github.com/ron-rs/ron/pull/498)) +- Add new extension `explicit_struct_names` which requires that struct names are included during deserialization ([#522](https://github.com/ron-rs/ron/pull/522)) +- Add new path-based field metadata serialization support via `PrettyConfig` ([#544](https://github.com/ron-rs/ron/pull/544)) +- Breaking: Change `PrettyConfig` so that `new_line`, `indentor` and `separator` are all `Cow<'static, str>` instead of `String` ([#546](https://github.com/ron-rs/ron/pull/546)) + +### Format Changes + +- [Non-API] Breaking: Treat `Some` like a newtype variant with `unwrap_variant_newtypes` ([#465](https://github.com/ron-rs/ron/pull/465)) +- Allow parsing floating point literals with underscores ([#481](https://github.com/ron-rs/ron/pull/481)) +- **Format-Breaking:** Switch from base64-encoded to Rusty byte strings, still allow base64 deserialising for now ([#438](https://github.com/ron-rs/ron/pull/438)) +- Fix issue [#241](https://github.com/ron-rs/ron/issues/241) and allow parsing numbers with explicit type suffixes, e.g. `1u8` or `-1f32` ([#481](https://github.com/ron-rs/ron/pull/481)) +- Add support for byte literals as strongly typed unsigned 8-bit integers ([#438](https://github.com/ron-rs/ron/pull/438)) +- Fix issue [#321](https://github.com/ron-rs/ron/issues/321) and allow parsing UTF-8 identifiers ([#488](https://github.com/ron-rs/ron/pull/488)) + +### Bug Fixes + +- Fix parsing `r` as a self-describing struct or variant name (and not the start of a raw string) ([#465](https://github.com/ron-rs/ron/pull/465)) +- Fix serialising raw strings containing a literal backslash ([#465](https://github.com/ron-rs/ron/pull/465)) +- Fix serialising `None` inside a stack of nested `Option`s with `#![enable(implicit_some)]` enabled ([#465](https://github.com/ron-rs/ron/pull/465)) +- Fix deserialising deserialising `A('/')` into a `ron::Value` ([#465](https://github.com/ron-rs/ron/pull/465)) +- Fix issue [#445](https://github.com/ron-rs/ron/issues/445) and allow parsing `+unsigned` as an unsigned int ([#479](https://github.com/ron-rs/ron/pull/479)) +- Fix serialising reserved identifiers `true`, `false`, `Some`, `None`, `inf`[`f32`|`f64`], and `Nan`[`f32`|`f64`] ([#487](https://github.com/ron-rs/ron/pull/487)) +- Disallow unclosed line comments at the end of `ron::value::RawValue` ([#489](https://github.com/ron-rs/ron/pull/489)) +- Fix parsing of struct/variant names starting in `None`, `Some`, `true`, or `false` ([#499](https://github.com/ron-rs/ron/pull/499)) +- Fix deserialising owned string field names in structs, allowing deserializing into `serde_json::Value`s ([#511](https://github.com/ron-rs/ron/pull/512)) + +### Miscellaneous + +- Add CIFuzz GitHub action ([#429](https://github.com/ron-rs/ron/pull/429)) +- Update the arbitrary fuzzer to check arbitrary serde data types, values, and `ron::ser::PrettyConfig`s ([#465](https://github.com/ron-rs/ron/pull/465)) +- Add a benchmark for PRs that runs over the latest fuzzer corpus ([#465](https://github.com/ron-rs/ron/pull/465)) +- Fuzz serde enum representations and collect current limitations in ron and serde ([#502](https://github.com/ron-rs/ron/pull/502)) +- Update `base64` dependency to version 0.22 ([#529](https://github.com/ron-rs/ron/pull/529)) +- Fix issue [#556](https://github.com/ron-rs/ron/issues/556) and update minium dependency versions ([#557](https://github.com/ron-rs/ron/pull/557)) + ## [0.8.1] - 2023-08-17 + - Fix issues [#277](https://github.com/ron-rs/ron/issues/277) and [#405](https://github.com/ron-rs/ron/issues/405) with `Value::Map` `IntoIter` and extraneous item check for `Value::Seq` ([#406](https://github.com/ron-rs/ron/pull/406)) - Fix issue [#401](https://github.com/ron-rs/ron/issues/401) with correct raw struct name identifier parsing ([#402](https://github.com/ron-rs/ron/pull/402)) - Fix issue [#410](https://github.com/ron-rs/ron/issues/410) trailing comma parsing in tuples and `Some` ([#412](https://github.com/ron-rs/ron/pull/412)) diff --git a/third_party/rust/ron/Cargo.lock b/third_party/rust/ron/Cargo.lock index f0b6bf1bc03c..38b8436af83f 100644 --- a/third_party/rust/ron/Cargo.lock +++ b/third_party/rust/ron/Cargo.lock @@ -4,42 +4,61 @@ version = 3 [[package]] name = "base64" -version = "0.21.2" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "604178f6c5c21f02dc555784810edfb88d34ac2c73b2eae109655649ee73ce3d" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" -version = "2.4.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +dependencies = [ + "serde", +] + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" dependencies = [ "serde", ] [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "erased-serde" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7" +dependencies = [ + "serde", + "typeid", +] [[package]] name = "hashbrown" -version = "0.14.0" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" [[package]] name = "heck" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "indexmap" -version = "2.0.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" +checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" dependencies = [ "equivalent", "hashbrown", @@ -47,16 +66,37 @@ dependencies = [ ] [[package]] -name = "itoa" -version = "1.0.9" +name = "inventory" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "ab08d7cd2c5897f2c949e5383ea7c7db03fb19130ffcfbf7eda795137ae3cb83" +dependencies = [ + "rustversion", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "once_cell" +version = "1.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75b0bedcc4fe52caa0e03d9f1151a323e4aa5e2d78ba3580400cd3c9e2bc4bc" [[package]] name = "option_set" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6086504539517779cabafa311a1092e743ef0e8e8aad859db384f06ceb1ada44" +checksum = "a6f45dc15724e4f030fde1e2017fa675144e04aa65b7f1e5596554826afbc06e" dependencies = [ "heck", "serde", @@ -64,65 +104,74 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] [[package]] name = "ron" -version = "0.8.1" +version = "0.9.0" dependencies = [ "base64", "bitflags", + "bytes", "indexmap", "option_set", "serde", "serde_bytes", "serde_derive", "serde_json", + "typetag", + "unicode-ident", ] [[package]] -name = "ryu" -version = "1.0.15" +name = "rustversion" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "serde" -version = "1.0.183" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32ac8da02677876d532745a130fc9d8e6edfa81a269b107c5b00829b91d8eb3c" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_bytes" -version = "0.11.12" +version = "0.11.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab33ec92f677585af6d88c65593ae2375adde54efdbf16d597f2cbc7a6d368ff" +checksum = "8437fd221bde2d4ca316d61b90e337e9e702b3820b87d63caa9ba6c02bd06d96" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.183" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aafe972d60b0b9bee71a91b92fee2d4fb3c9d7e8f6b179aa99f27203d99a4816" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", @@ -131,20 +180,21 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.105" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] [[package]] name = "syn" -version = "2.0.29" +version = "2.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" dependencies = [ "proc-macro2", "quote", @@ -152,7 +202,37 @@ dependencies = [ ] [[package]] -name = "unicode-ident" -version = "1.0.11" +name = "typeid" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + +[[package]] +name = "typetag" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f22b40dd7bfe8c14230cf9702081366421890435b2d625fa92b4acc4c3de6f" +dependencies = [ + "erased-serde", + "inventory", + "once_cell", + "serde", + "typetag-impl", +] + +[[package]] +name = "typetag-impl" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35f5380909ffc31b4de4f4bdf96b877175a016aa2ca98cee39fcfd8c4d53d952" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" diff --git a/third_party/rust/ron/Cargo.toml b/third_party/rust/ron/Cargo.toml index 5879d23a5de5..9060d4478d64 100644 --- a/third_party/rust/ron/Cargo.toml +++ b/third_party/rust/ron/Cargo.toml @@ -13,13 +13,19 @@ edition = "2021" rust-version = "1.64.0" name = "ron" -version = "0.8.1" +version = "0.9.0" authors = [ "Christopher Durham ", "Dzmitry Malyshau ", "Thomas Schaller ", "Juniper Tyree ", ] +build = false +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false description = "Rusty Object Notation" homepage = "https://github.com/ron-rs/ron" documentation = "https://docs.rs/ron/" @@ -33,37 +39,376 @@ categories = ["encoding"] license = "MIT OR Apache-2.0" repository = "https://github.com/ron-rs/ron" -[dependencies.base64] -version = "0.21" - -[dependencies.bitflags] -version = "2.0" -features = ["serde"] - -[dependencies.indexmap] -version = "2.0" -features = ["serde"] -optional = true - -[dependencies.serde] -version = "1.0.60" - -[dependencies.serde_derive] -version = "1.0" - -[dev-dependencies.option_set] -version = "0.2" - -[dev-dependencies.serde] -version = "1.0" -features = ["derive"] - -[dev-dependencies.serde_bytes] -version = "0.11" - -[dev-dependencies.serde_json] -version = "1.0" +[package.metadata.docs.rs] +features = [ + "integer128", + "indexmap", +] +rustdoc-args = ["--generate-link-to-definition"] [features] default = [] integer128 = [] + +[lib] +name = "ron" +path = "src/lib.rs" + +[[example]] +name = "base64" +path = "examples/base64.rs" + +[[example]] +name = "decode" +path = "examples/decode.rs" + +[[example]] +name = "decode_file" +path = "examples/decode_file.rs" + +[[example]] +name = "encode" +path = "examples/encode.rs" + +[[example]] +name = "transcode" +path = "examples/transcode.rs" + +[[test]] +name = "115_minimal_flattening" +path = "tests/115_minimal_flattening.rs" + +[[test]] +name = "117_untagged_tuple_variant" +path = "tests/117_untagged_tuple_variant.rs" + +[[test]] +name = "123_enum_representation" +path = "tests/123_enum_representation.rs" + +[[test]] +name = "129_indexmap" +path = "tests/129_indexmap.rs" + +[[test]] +name = "147_empty_sets_serialisation" +path = "tests/147_empty_sets_serialisation.rs" + +[[test]] +name = "152_bitflags" +path = "tests/152_bitflags.rs" + +[[test]] +name = "203_error_positions" +path = "tests/203_error_positions.rs" + +[[test]] +name = "207_adjacently_tagged_enum" +path = "tests/207_adjacently_tagged_enum.rs" + +[[test]] +name = "217_nested_untagged_enums" +path = "tests/217_nested_untagged_enums.rs" + +[[test]] +name = "238_array" +path = "tests/238_array.rs" + +[[test]] +name = "240_array_pretty" +path = "tests/240_array_pretty.rs" + +[[test]] +name = "250_variant_newtypes" +path = "tests/250_variant_newtypes.rs" + +[[test]] +name = "254_typetag" +path = "tests/254_typetag.rs" + +[[test]] +name = "256_comma_error" +path = "tests/256_comma_error.rs" + +[[test]] +name = "289_enumerate_arrays" +path = "tests/289_enumerate_arrays.rs" + +[[test]] +name = "301_struct_name_mismatch" +path = "tests/301_struct_name_mismatch.rs" + +[[test]] +name = "307_stack_overflow" +path = "tests/307_stack_overflow.rs" + +[[test]] +name = "321_unicode_ident" +path = "tests/321_unicode_ident.rs" + +[[test]] +name = "322_escape_idents" +path = "tests/322_escape_idents.rs" + +[[test]] +name = "337_value_float_roundtrip" +path = "tests/337_value_float_roundtrip.rs" + +[[test]] +name = "357_untagged_enum_roundtrip" +path = "tests/357_untagged_enum_roundtrip.rs" + +[[test]] +name = "359_deserialize_seed" +path = "tests/359_deserialize_seed.rs" + +[[test]] +name = "367_implicit_some" +path = "tests/367_implicit_some.rs" + +[[test]] +name = "370_float_parsing" +path = "tests/370_float_parsing.rs" + +[[test]] +name = "393_serde_errors" +path = "tests/393_serde_errors.rs" + +[[test]] +name = "401_raw_identifier" +path = "tests/401_raw_identifier.rs" + +[[test]] +name = "407_raw_value" +path = "tests/407_raw_value.rs" + +[[test]] +name = "410_trailing_comma" +path = "tests/410_trailing_comma.rs" + +[[test]] +name = "423_de_borrowed_identifier" +path = "tests/423_de_borrowed_identifier.rs" + +[[test]] +name = "425_escape_strings" +path = "tests/425_escape_strings.rs" + +[[test]] +name = "436_untagged_bytes" +path = "tests/436_untagged_bytes.rs" + +[[test]] +name = "438_rusty_byte_strings" +path = "tests/438_rusty_byte_strings.rs" + +[[test]] +name = "447_compact_maps_structs" +path = "tests/447_compact_maps_structs.rs" + +[[test]] +name = "449_tagged_enum" +path = "tests/449_tagged_enum.rs" + +[[test]] +name = "462_bytes" +path = "tests/462_bytes.rs" + +[[test]] +name = "465_implicit_some_stack" +path = "tests/465_implicit_some_stack.rs" + +[[test]] +name = "465_no_comment_char_value" +path = "tests/465_no_comment_char_value.rs" + +[[test]] +name = "465_r_name_value" +path = "tests/465_r_name_value.rs" + +[[test]] +name = "465_ser_backslash_string" +path = "tests/465_ser_backslash_string.rs" + +[[test]] +name = "465_unwrap_some_newtype_variant_value" +path = "tests/465_unwrap_some_newtype_variant_value.rs" + +[[test]] +name = "465_validate_ser_identifiers" +path = "tests/465_validate_ser_identifiers.rs" + +[[test]] +name = "481_number_underscores_suffixes" +path = "tests/481_number_underscores_suffixes.rs" + +[[test]] +name = "492_enum_in_untagged_enum" +path = "tests/492_enum_in_untagged_enum.rs" + +[[test]] +name = "502_known_bugs" +path = "tests/502_known_bugs.rs" + +[[test]] +name = "508_value_adjacently_tagged_bug" +path = "tests/508_value_adjacently_tagged_bug.rs" + +[[test]] +name = "511_deserialize_any_map_string_key" +path = "tests/511_deserialize_any_map_string_key.rs" + +[[test]] +name = "522_explicit_struct_names" +path = "tests/522_explicit_struct_names.rs" + +[[test]] +name = "526_flatten" +path = "tests/526_flatten.rs" + +[[test]] +name = "530_untagged_union" +path = "tests/530_untagged_union.rs" + +[[test]] +name = "544_path_meta" +path = "tests/544_path_meta.rs" + +[[test]] +name = "big_struct" +path = "tests/big_struct.rs" + +[[test]] +name = "borrowed_str" +path = "tests/borrowed_str.rs" + +[[test]] +name = "comments" +path = "tests/comments.rs" + +[[test]] +name = "depth_limit" +path = "tests/depth_limit.rs" + +[[test]] +name = "escape" +path = "tests/escape.rs" + +[[test]] +name = "extensions" +path = "tests/extensions.rs" + +[[test]] +name = "floats" +path = "tests/floats.rs" + +[[test]] +name = "large_number" +path = "tests/large_number.rs" + +[[test]] +name = "min_max" +path = "tests/min_max.rs" + +[[test]] +name = "non_identifier_identifier" +path = "tests/non_identifier_identifier.rs" + +[[test]] +name = "non_string_tag" +path = "tests/non_string_tag.rs" + +[[test]] +name = "numbers" +path = "tests/numbers.rs" + +[[test]] +name = "options" +path = "tests/options.rs" + +[[test]] +name = "preserve_sequence" +path = "tests/preserve_sequence.rs" + +[[test]] +name = "roundtrip" +path = "tests/roundtrip.rs" + +[[test]] +name = "struct_integers" +path = "tests/struct_integers.rs" + +[[test]] +name = "to_string_pretty" +path = "tests/to_string_pretty.rs" + +[[test]] +name = "unicode" +path = "tests/unicode.rs" + +[[test]] +name = "value" +path = "tests/value.rs" + +[dependencies.base64] +version = "0.22" +features = ["std"] +default-features = false + +[dependencies.bitflags] +version = "2.1" +features = ["serde"] +default-features = false + +[dependencies.indexmap] +version = "2.0" +features = [ + "std", + "serde", +] +optional = true +default-features = false + +[dependencies.serde] +version = "1.0.181" +features = ["std"] +default-features = false + +[dependencies.serde_derive] +version = "1.0.181" +default-features = false + +[dependencies.unicode-ident] +version = "1.0" +default-features = false + +[dev-dependencies.bytes] +version = "1.3" +features = ["serde"] +default-features = false + +[dev-dependencies.option_set] +version = "0.3" +default-features = false + +[dev-dependencies.serde] +version = "1.0.181" +features = [ + "std", + "derive", +] +default-features = false + +[dev-dependencies.serde_bytes] +version = "0.11" +features = ["std"] +default-features = false + +[dev-dependencies.serde_json] +version = "1.0.60" +features = ["std"] +default-features = false + +[dev-dependencies.typetag] +version = "0.2" +default-features = false diff --git a/third_party/rust/ron/README.md b/third_party/rust/ron/README.md index b7842226268b..9646e8cdf454 100644 --- a/third_party/rust/ron/README.md +++ b/third_party/rust/ron/README.md @@ -1,10 +1,13 @@ # Rusty Object Notation -[![CI](https://github.com/ron-rs/ron/actions/workflows/ci.yaml/badge.svg)](https://github.com/ron-rs/ron/actions/workflows/ci.yaml) -[![codecov](https://img.shields.io/codecov/c/github/ron-rs/ron/codecov?token=x4Q5KA51Ul)](https://codecov.io/gh/ron-rs/ron) -[![Crates.io](https://img.shields.io/crates/v/ron.svg)](https://crates.io/crates/ron) [![MSRV](https://img.shields.io/badge/MSRV-1.64.0-orange)](https://github.com/ron-rs/ron) +[![Crates.io](https://img.shields.io/crates/v/ron.svg)](https://crates.io/crates/ron) [![Docs](https://docs.rs/ron/badge.svg)](https://docs.rs/ron) + +[![CI](https://github.com/ron-rs/ron/actions/workflows/ci.yaml/badge.svg)](https://github.com/ron-rs/ron/actions/workflows/ci.yaml) +[![Coverage](https://img.shields.io/endpoint?url=https%3A%2F%2Fron-rs.github.io%2Fron%2Fcoverage%2Fcoverage.json)](https://ron-rs.github.io/ron/coverage/) +[![Fuzzing](https://oss-fuzz-build-logs.storage.googleapis.com/badges/ron.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:ron) + [![Matrix](https://img.shields.io/matrix/ron-rs:matrix.org.svg)](https://matrix.to/#/#ron-rs:matrix.org) RON is a simple readable data serialization format that looks similar to Rust syntax. @@ -13,7 +16,7 @@ structs, enums, tuples, arrays, generic maps, and primitive values. ## Example -```rust,ignore +```ron GameConfig( // optional struct name window_size: (800, 600), window_title: "PAC-MAN", @@ -42,6 +45,30 @@ GameConfig( // optional struct name ) ``` +## RON syntax overview + +* Numbers: `42`, `3.14`, `0xFF`, `0b0110` +* Strings: `"Hello"`, `"with\\escapes\n"`, `r#"raw string, great for regex\."#` +* Byte Strings: `b"Hello"`, `b"with \x65\x73\x63\x61\x70\x65\x73\n"`, `br#"raw, too"#` +* Booleans: `true`, `false` +* Chars: `'e'`, `'\n'` +* Optionals: `Some("string")`, `Some(Some(1.34))`, `None` +* Tuples: `("abc", 1.23, true)`, `()` +* Lists: `["abc", "def"]` +* Structs: `( foo: 1.0, bar: ( baz: "I'm nested" ) )` +* Maps: `{ "arbitrary": "keys", "are": "allowed" }` + +> **Note:** Serde's data model represents fixed-size Rust arrays as tuple (instead of as list) + +RON also supports several extensions, which are documented [here](docs/extensions.md). + +## Specification + +RON's formal and complete grammar is available [here](docs/grammar.md). + +There also is a very basic, work in progress specification available on +[the wiki page](https://github.com/ron-rs/ron/wiki/Specification). + ## Why RON? ### Example in JSON @@ -71,7 +98,7 @@ GameConfig( // optional struct name ### Same example in RON -```rust,ignore +```ron Scene( // class name is optional materials: { // this is a map "metal": ( @@ -102,27 +129,6 @@ Note the following advantages of RON over JSON: * optional struct names improve readability * enums are supported (and less verbose than their JSON representation) -## Limitations - -RON is not designed to be a fully self-describing format (unlike JSON) and is thus not guaranteed to work when [`deserialize_any`](https://docs.rs/serde/latest/serde/trait.Deserializer.html#tymethod.deserialize_any) is used instead of its typed alternatives. In particular, the following Serde attributes are not yet supported: -- `#[serde(tag = "type")]`, i.e. internally tagged enums -- `#[serde(untagged)]`, i.e. untagged enums -- `#[serde(flatten)]`, i.e. flattening an inner struct into its outer container - -## RON syntax overview - -* Numbers: `42`, `3.14`, `0xFF`, `0b0110` -* Strings: `"Hello"`, `"with\\escapes\n"`, `r#"raw string, great for regex\."#` -* Booleans: `true`, `false` -* Chars: `'e'`, `'\n'` -* Optionals: `Some("string")`, `Some(Some(1.34))`, `None` -* Tuples: `("abc", 1.23, true)`, `()` -* Lists: `["abc", "def"]` -* Structs: `( foo: 1.0, bar: ( baz: "I'm nested" ) )` -* Maps: `{ "arbitrary": "keys", "are": "allowed" }` - -> **Note:** Serde's data model represents fixed-size Rust arrays as tuple (instead of as list) - ## Quickstart ### `Cargo.toml` @@ -168,12 +174,60 @@ fn main() { [emacs-ron]: https://chiselapp.com/user/Hutzdog/repository/ron-mode/home -## Specification +## Limitations -There is a very basic, work in progress specification available on -[the wiki page](https://github.com/ron-rs/ron/wiki/Specification). -A more formal and complete grammar is available [here](docs/grammar.md). +RON is not designed to be a fully self-describing format (unlike JSON) and is thus not guaranteed to work when [`deserialize_any`](https://docs.rs/serde/latest/serde/trait.Deserializer.html#tymethod.deserialize_any) is used instead of its typed alternatives. In particular, the following Serde attributes only have limited support: +- `#[serde(tag = "tag")]`, i.e. internally tagged enums [^serde-enum-hack] +- `#[serde(tag = "tag", content = "content")]`, i.e. adjacently tagged enums [^serde-enum-hack] +- `#[serde(untagged)]`, i.e. untagged enums [^serde-enum-hack] +- `#[serde(flatten)]`, i.e. flattening of structs into maps [^serde-flatten-hack] + +While data structures with any of these attributes should generally roundtrip through RON, some restrictions apply [^serde-restrictions] and their textual representation may not always match your expectation: + +- ron only supports string keys inside maps flattened into structs +- internally (or adjacently) tagged or untagged enum variants or `#[serde(flatten)]`ed fields must not contain: + - struct names, e.g. by enabling the `#[enable(explicit_struct_names)]` extension or the `PrettyConfig::struct_names` setting + - newtypes + - zero-length arrays / tuples / tuple structs / structs / tuple variants / struct variants + - `Option`s with `#[enable(implicit_some)]` must not contain any of these or a unit, unit struct, or an untagged unit variant + - externally tagged tuple variants with just one field (that are not newtype variants) + - tuples or arrays or tuple structs with just one element are not supported inside newtype variants with `#[enable(unwrap_variant_newtypes)]` (including `Some`) + - a `ron::value::RawValue` +- untagged tuple / struct variants with no fields are not supported +- untagged tuple variants with just one field (that are not newtype variants) are not supported when the `#![enable(unwrap_variant_newtypes)]` extension is enabled +- serializing a `ron::value::RawValue` using a `PrettyConfig` may add leading and trailing whitespace and comments, which the `ron::value::RawValue` absorbs upon deserialization + +Furthermore, serde imposes the following restrictions for data to roundtrip: + +- structs or struct variants that contain a `#[serde(flatten)]`ed field: + - are only serialised as maps and deserialised from maps + - must not contain duplicate fields / keys, e.g. where an inner-struct field matches an outer-struct or inner-struct field + - must not contain more than one (within the super-struct of all flattened structs) `#[serde(flatten)]`ed map field, which collects all unknown fields + - if they contain a `#[serde(flatten)]`ed map, they must not contain: + - a struct that is not flattened itself but contains some flattened fields and is flattened into the outer struct (variant) + - an untagged struct variant that contains some flattened fields + - a flattened externally tagged newtype, tuple, or struct variant, flattened internally tagged unit, newtype, or struct variant, or any flattened adjacently tagged variant + - a flattened tagged struct +- internally (or adjacently) tagged or untagged enum variants or `#[serde(flatten)]`ed fields must not contain: + - `i128` or `u128` values +- internally tagged newtype variants and `#[serde(flatten)]`ed fields must not contain: + - a unit or a unit struct inside an untagged newtype variant + - an untagged unit variant +- internally tagged newtype variants, which are `#[serde(flatten)]`ed together with other fields, must not contain: + - a unit or unit struct or an untagged unit variant + +While RON offers a best-effort implementation for `#[serde(flatten)]`, it may be unsupported in further cases and combinations not listed above. These limitations stem primarily from serde rather than RON. Enumerating all such cases based on serde's behavior is nontrivial, so the lists above are not exhaustive. + +Please file a [new issue](https://github.com/ron-rs/ron/issues/new) if you come across a use case which is not listed among the above restrictions but still breaks. + +While RON guarantees roundtrips like Rust -> RON -> Rust for Rust types using non-`deserialize_any`-based implementations, RON does not yet make any guarantees about roundtrips through `ron::Value`. For instance, even when RON -> Rust works, RON -> `ron::Value` -> Rust, or RON -> `ron::Value` -> RON -> Rust may not work. We plan on improving `ron::Value` in an upcoming version of RON, though this work is partially blocked on [serde#1183](https://github.com/serde-rs/serde/issues/1183). + +[^serde-enum-hack]: Deserialising an internally, adjacently, or un-tagged enum requires detecting `serde`'s internal `serde::__private::de::content::Content` content type so that RON can describe the deserialised data structure in serde's internal JSON-like format. This detection only works for the automatically-derived [`Deserialize`](https://docs.rs/serde/latest/serde/de/trait.Deserialize.html) impls on enums. See [#451](https://github.com/ron-rs/ron/pull/451) for more details. + +[^serde-flatten-hack]: Deserialising a flattened struct from a map requires that the struct's [`Visitor::expecting`](https://docs.rs/serde/latest/serde/de/trait.Visitor.html#tymethod.expecting) implementation formats a string starting with `"struct "`. This is the case for automatically-derived [`Deserialize`](https://docs.rs/serde/latest/serde/de/trait.Deserialize.html) impls on structs. See [#455](https://github.com/ron-rs/ron/pull/455) for more details. + +[^serde-restrictions]: Most of these restrictions are currently blocked on [serde#1183](https://github.com/serde-rs/serde/issues/1183), which limits non-self-describing formats from roundtripping format-specific information through internally (or adjacently) tagged or untagged enums or `#[serde(flatten)]`ed fields. ## License diff --git a/third_party/rust/ron/clippy.toml b/third_party/rust/ron/clippy.toml index cb8cfcd1c742..22fd4be7375d 100644 --- a/third_party/rust/ron/clippy.toml +++ b/third_party/rust/ron/clippy.toml @@ -1,2 +1 @@ msrv = "1.64.0" -blacklisted-names = [] diff --git a/third_party/rust/ron/docs/extensions.md b/third_party/rust/ron/docs/extensions.md index a849220222cb..aa20f5e6f60c 100644 --- a/third_party/rust/ron/docs/extensions.md +++ b/third_party/rust/ron/docs/extensions.md @@ -21,7 +21,7 @@ struct Object { Without `unwrap_newtypes`, because the value `5` can not be saved into `NewType(u32)`, your RON document would look like this: -``` ron +```ron ( new_type: (5), ) @@ -29,7 +29,7 @@ Without `unwrap_newtypes`, because the value `5` can not be saved into `NewType( With the `unwrap_newtypes` extension, this coercion is done automatically. So `5` will be interpreted as `(5)`. -``` ron +```ron #![enable(unwrap_newtypes)] ( new_type: 5, @@ -98,7 +98,7 @@ pub enum Enum { Without `unwrap_variant_newtypes`, your RON document would look like this: -``` ron +```ron ( variant: A(Inner(a: 4, b: true)), ) @@ -106,7 +106,7 @@ Without `unwrap_variant_newtypes`, your RON document would look like this: With the `unwrap_variant_newtypes` extension, the first structural layer inside a newtype variant will be unwrapped automatically: -``` ron +```ron #![enable(unwrap_newtypes)] ( variant: A(a: 4, b: true), @@ -114,3 +114,47 @@ With the `unwrap_variant_newtypes` extension, the first structural layer inside ``` Note that when the `unwrap_variant_newtypes` extension is enabled, the first layer inside a newtype variant will **always** be unwrapped, i.e. it is no longer possible to write `A(Inner(a: 4, b: true))` or `A((a: 4, b: true))`. + +# explicit_struct_names +During serialization, this extension emits struct names. For instance, this would be emitted: +```ron +Foo( + bar: Bar(42), +) +``` + +During deserialization, this extension requires that all structs have names attached to them. For example, the following deserializes perfectly fine: +```ron +Foo( + bar: Bar(42), +) +``` + +However, with the `explicit_struct_names` extension enabled, the following will throw an `ExpectedStructName` error: +```ron +( + bar: Bar(42), +) +``` + +Similarly, the following will throw the same error: +```ron +Foo( + bar: (42), +) +``` + +Note that if what you are parsing is spread across many files, you would likely use `Options::with_default_extension` to enable `Extensions::EXPLICIT_STRUCT_NAMES` before the parsing stage. This is because prepending `#![enable(explicit_struct_names)]` to the contents of every file you parse would violate DRY (Don't Repeat Yourself). + +Here is an example of how to enable `explicit_struct_names` using this method: +```rust +use ron::extensions::Extensions; +use ron::options::Options; + +// Setup the options +let options = Options::default().with_default_extension(Extensions::EXPLICIT_STRUCT_NAMES); +// Retrieve the contents of the file +let file_contents: &str = /* ... */; +// Parse the file's contents +let foo: Foo = options.from_str(file_contents)?; +``` diff --git a/third_party/rust/ron/docs/grammar.md b/third_party/rust/ron/docs/grammar.md index 3ef2b7f7e106..1d0e6cc5f4af 100644 --- a/third_party/rust/ron/docs/grammar.md +++ b/third_party/rust/ron/docs/grammar.md @@ -17,7 +17,7 @@ RON = [extensions], ws, value, ws; ```ebnf ws = { ws_single | comment }; -ws_single = "\n" | "\t" | "\r" | " "; +ws_single = "\n" | "\t" | "\r" | " " | U+000B | U+000C | U+0085 | U+200E | U+200F | U+2028 | U+2029; comment = ["//", { no_newline }, "\n"] | ["/*", nested_block_comment, "*/"]; nested_block_comment = { ? any characters except "/*" or "*/" ? }, [ "/*", nested_block_comment, "*/", nested_block_comment ]; ``` @@ -40,33 +40,52 @@ For the extension names see the [`extensions.md`][exts] document. ## Value ```ebnf -value = unsigned | signed | float | string | char | bool | option | list | map | tuple | struct | enum_variant; +value = integer | byte | float | string | byte_string | char | bool | option | list | map | tuple | struct | enum_variant; ``` ## Numbers ```ebnf digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"; -hex_digit = "A" | "a" | "B" | "b" | "C" | "c" | "D" | "d" | "E" | "e" | "F" | "f"; -unsigned = (["0", ("b" | "o")], digit, { digit | '_' } | - "0x", (digit | hex_digit), { digit | hex_digit | '_' }); -signed = ["+" | "-"], unsigned; -float = ["+" | "-"], ("inf" | "NaN" | float_num); +digit_binary = "0" | "1"; +digit_octal = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7"; +digit_hexadecimal = digit | "A" | "a" | "B" | "b" | "C" | "c" | "D" | "d" | "E" | "e" | "F" | "f"; + +integer = ["+" | "-"], unsigned, [integer_suffix]; +integer_suffix = ("i", "u"), ("8", "16", "32", "64", "128"); + +unsigned = unsigned_binary | unsigned_octal | unsigned_hexadecimal | unsigned_decimal; +unsigned_binary = "0b", digit_binary, { digit_binary | "_" }; +unsigned_octal = "0o", digit_octal, { digit_octal | "_" }; +unsigned_hexadecimal = "0x", digit_hexadecimal, { digit_hexadecimal | "_" }; +unsigned_decimal = digit, { digit | "_" }; + +byte = "b", "'", byte_content, "'"; +byte_content = ascii | ("\\", (escape_ascii | escape_byte)); + +float = ["+" | "-"], ("inf" | "NaN" | float_num), [float_suffix]; float_num = (float_int | float_std | float_frac), [float_exp]; -float_int = digit, { digit }; -float_std = digit, { digit }, ".", {digit}; -float_frac = ".", digit, {digit}; -float_exp = ("e" | "E"), ["+" | "-"], digit, {digit}; +float_int = digit, { digit | "_" }; +float_std = digit, { digit | "_" }, ".", [digit, { digit | "_" }]; +float_frac = ".", digit, { digit | "_" }; +float_exp = ("e" | "E"), ["+" | "-"], { digit | "_" }, digit, { digit | "_" }; +float_suffix = "f", ("32", "64"); ``` +> Note: `ascii` refers to any ASCII character, i.e. any byte in range `0x00 ..= 0x7F`. + ## String ```ebnf string = string_std | string_raw; string_std = "\"", { no_double_quotation_marks | string_escape }, "\""; -string_escape = "\\", ("\"" | "\\" | "b" | "f" | "n" | "r" | "t" | ("u", unicode_hex)); -string_raw = "r" string_raw_content; +string_escape = "\\", (escape_ascii | escape_byte | escape_unicode); +string_raw = "r", string_raw_content; string_raw_content = ("#", string_raw_content, "#") | "\"", { unicode_non_greedy }, "\""; + +escape_ascii = "'" | "\"" | "\\" | "n" | "r" | "t" | "0"; +escape_byte = "x", digit_hexadecimal, digit_hexadecimal; +escape_unicode = "u", digit_hexadecimal, [digit_hexadecimal, [digit_hexadecimal, [digit_hexadecimal, [digit_hexadecimal, [digit_hexadecimal]]]]]; ``` > Note: Raw strings start with an `r`, followed by n `#`s and a quotation mark @@ -83,6 +102,24 @@ Also see [the Rust document] about context-sensitivity of raw strings. [the Rust document]: https://github.com/rust-lang/rust/blob/d046ffddc4bd50e04ffc3ff9f766e2ac71f74d50/src/grammar/raw-string-literal-ambiguity.md +## Byte String + +```ebnf +byte_string = byte_string_std | byte_string_raw; +byte_string_std = "b\"", { no_double_quotation_marks | string_escape }, "\""; +byte_string_raw = "br", string_raw_content; +``` + +> Note: Byte strings are similar to normal strings but are not required to + contain only valid UTF-8 text. RON's byte strings follow the updated Rust + byte string literal rules as proposed in [RFC #3349], i.e. byte strings + allow the exact same characters and escape codes as normal strings. + +[RFC #3349](https://github.com/rust-lang/rfcs/pull/3349) + +> Note: Raw byte strings start with an `br` prefix and follow the same rules + as raw strings, which are outlined above. + ## Char ```ebnf @@ -145,8 +182,10 @@ enum_variant_named = ident, ws, "(", [named_field, { comma, named_field }, [comm ```ebnf ident = ident_std | ident_raw; ident_std = ident_std_first, { ident_std_rest }; -ident_std_first = "A" | ... | "Z" | "a" | ... | "z" | "_"; -ident_std_rest = ident_std_first | digit; +ident_std_first = XID_Start | "_"; +ident_std_rest = XID_Continue; ident_raw = "r", "#", ident_raw_rest, { ident_raw_rest }; ident_raw_rest = ident_std_rest | "." | "+" | "-"; ``` + +> Note: [XID_Start](http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AXID_Start%3A%5D&abb=on&g=&i=) and [XID_Continue](http://unicode.org/cldr/utility/list-unicodeset.jsp?a=%5B%3AXID_Continue%3A%5D&abb=on&g=&i=) refer to Unicode character sets. diff --git a/third_party/rust/ron/examples/base64.rs b/third_party/rust/ron/examples/base64.rs new file mode 100644 index 000000000000..9349d17fb684 --- /dev/null +++ b/third_party/rust/ron/examples/base64.rs @@ -0,0 +1,146 @@ +//! ron initially encoded byte-slices and byte-bufs as base64-encoded strings. +//! However, since v0.9, ron now uses Rusty byte string literals instead. +//! +//! This example shows how the previous behaviour can be restored by serialising +//! bytes with strongly-typed base64-encoded strings, or accepting both Rusty +//! byte strings and the legacy base64-encoded string syntax. + +use base64::engine::{general_purpose::STANDARD as BASE64, Engine}; +use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +struct Config { + #[serde(with = "ByteStr")] + bytes: Vec, + #[serde(with = "Base64")] + base64: Vec, + #[serde(with = "ByteStrOrBase64")] + bytes_or_base64: Vec, +} + +enum ByteStr {} + +impl ByteStr { + fn serialize(data: &[u8], serializer: S) -> Result { + serializer.serialize_bytes(data) + } + + fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result, D::Error> { + struct ByteStrVisitor; + + impl<'de> Visitor<'de> for ByteStrVisitor { + type Value = Vec; + + fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.write_str("a Rusty byte string") + } + + fn visit_bytes(self, bytes: &[u8]) -> Result { + Ok(bytes.to_vec()) + } + + fn visit_byte_buf(self, bytes: Vec) -> Result { + Ok(bytes) + } + } + + deserializer.deserialize_byte_buf(ByteStrVisitor) + } +} + +enum Base64 {} + +impl Base64 { + fn serialize(data: &[u8], serializer: S) -> Result { + serializer.serialize_str(&BASE64.encode(data)) + } + + fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result, D::Error> { + let base64_str = <&str>::deserialize(deserializer)?; + BASE64.decode(base64_str).map_err(serde::de::Error::custom) + } +} + +enum ByteStrOrBase64 {} + +impl ByteStrOrBase64 { + fn serialize(data: &[u8], serializer: S) -> Result { + if cfg!(all()) { + // either of these would work + serializer.serialize_str(&BASE64.encode(data)) + } else { + serializer.serialize_bytes(data) + } + } + + fn deserialize<'de, D: Deserializer<'de>>(deserializer: D) -> Result, D::Error> { + struct ByteStrOrBase64Visitor; + + impl<'de> Visitor<'de> for ByteStrOrBase64Visitor { + type Value = Vec; + + fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.write_str("a Rusty byte string or a base64-encoded string") + } + + fn visit_str(self, base64_str: &str) -> Result { + BASE64.decode(base64_str).map_err(serde::de::Error::custom) + } + + fn visit_bytes(self, bytes: &[u8]) -> Result { + Ok(bytes.to_vec()) + } + + fn visit_byte_buf(self, bytes: Vec) -> Result { + Ok(bytes) + } + } + + deserializer.deserialize_any(ByteStrOrBase64Visitor) + } +} + +fn main() { + let ron = r#"Config( + bytes: b"only byte strings are allowed", + base64: "b25seSBiYXNlNjQtZW5jb2RlZCBzdHJpbmdzIGFyZSBhbGxvd2Vk", + bytes_or_base64: b"both byte strings and base64-encoded strings work", + )"#; + + assert_eq!( + ron::from_str::(ron).unwrap(), + Config { + bytes: b"only byte strings are allowed".to_vec(), + base64: b"only base64-encoded strings are allowed".to_vec(), + bytes_or_base64: b"both byte strings and base64-encoded strings work".to_vec() + } + ); + + let ron = r#"Config( + bytes: b"only byte strings are allowed", + base64: "b25seSBiYXNlNjQtZW5jb2RlZCBzdHJpbmdzIGFyZSBhbGxvd2Vk", + bytes_or_base64: "Ym90aCBieXRlIHN0cmluZ3MgYW5kIGJhc2U2NC1lbmNvZGVkIHN0cmluZ3Mgd29yaw==", + )"#; + + assert_eq!( + ron::from_str::(ron).unwrap(), + Config { + bytes: b"only byte strings are allowed".to_vec(), + base64: b"only base64-encoded strings are allowed".to_vec(), + bytes_or_base64: b"both byte strings and base64-encoded strings work".to_vec() + } + ); + + println!( + "{}", + ron::ser::to_string_pretty( + &Config { + bytes: b"only byte strings are allowed".to_vec(), + base64: b"only base64-encoded strings are allowed".to_vec(), + bytes_or_base64: b"both byte strings and base64-encoded strings work".to_vec() + }, + ron::ser::PrettyConfig::default().struct_names(true) + ) + .unwrap() + ); +} diff --git a/third_party/rust/ron/examples/decode_file.rs b/third_party/rust/ron/examples/decode_file.rs index 72ca8a05f372..0f5169083270 100644 --- a/third_party/rust/ron/examples/decode_file.rs +++ b/third_party/rust/ron/examples/decode_file.rs @@ -23,7 +23,7 @@ struct Nested { fn main() { let input_path = format!("{}/examples/example.ron", env!("CARGO_MANIFEST_DIR")); - let f = File::open(&input_path).expect("Failed opening file"); + let f = File::open(input_path).expect("Failed opening file"); let config: Config = match from_reader(f) { Ok(x) => x, Err(e) => { diff --git a/third_party/rust/ron/src/de/id.rs b/third_party/rust/ron/src/de/id.rs index e7230da5fc5b..9fa20ecde862 100644 --- a/third_party/rust/ron/src/de/id.rs +++ b/third_party/rust/ron/src/de/id.rs @@ -1,25 +1,31 @@ use serde::de::{self, Visitor}; -use super::{Deserializer, Error, Result}; +use super::{Error, Result}; -pub struct IdDeserializer<'a, 'b: 'a> { - d: &'a mut Deserializer<'b>, +pub struct Deserializer<'a, 'b: 'a> { + de: &'a mut super::Deserializer<'b>, + map_as_struct: bool, } -impl<'a, 'b: 'a> IdDeserializer<'a, 'b> { - pub fn new(d: &'a mut Deserializer<'b>) -> Self { - IdDeserializer { d } +impl<'a, 'b: 'a> Deserializer<'a, 'b> { + pub fn new(de: &'a mut super::Deserializer<'b>, map_as_struct: bool) -> Self { + Deserializer { de, map_as_struct } } } -impl<'a, 'b: 'a, 'c> de::Deserializer<'b> for &'c mut IdDeserializer<'a, 'b> { +impl<'a, 'b: 'a, 'c> de::Deserializer<'b> for &'c mut Deserializer<'a, 'b> { type Error = Error; fn deserialize_identifier(self, visitor: V) -> Result where V: Visitor<'b>, { - self.d.deserialize_identifier(visitor) + if self.map_as_struct { + // We only allow string keys in flattened structs and maps + self.de.deserialize_str(visitor) + } else { + self.de.deserialize_identifier(visitor) + } } fn deserialize_str(self, visitor: V) -> Result @@ -29,6 +35,13 @@ impl<'a, 'b: 'a, 'c> de::Deserializer<'b> for &'c mut IdDeserializer<'a, 'b> { self.deserialize_identifier(visitor) } + fn deserialize_string(self, visitor: V) -> Result + where + V: Visitor<'b>, + { + self.deserialize_identifier(visitor) + } + fn deserialize_any(self, visitor: V) -> Result where V: Visitor<'b>, @@ -136,13 +149,6 @@ impl<'a, 'b: 'a, 'c> de::Deserializer<'b> for &'c mut IdDeserializer<'a, 'b> { Err(Error::ExpectedIdentifier) } - fn deserialize_string(self, _: V) -> Result - where - V: Visitor<'b>, - { - Err(Error::ExpectedIdentifier) - } - fn deserialize_bytes(self, _: V) -> Result where V: Visitor<'b>, diff --git a/third_party/rust/ron/src/de/mod.rs b/third_party/rust/ron/src/de/mod.rs index c0e62494be87..6028236b65ad 100644 --- a/third_party/rust/ron/src/de/mod.rs +++ b/third_party/rust/ron/src/de/mod.rs @@ -1,19 +1,20 @@ /// Deserialization module. -use std::{borrow::Cow, io, str}; +use std::{ + io::{self, Write}, + str, +}; -use base64::Engine; use serde::{ de::{self, DeserializeSeed, Deserializer as _, Visitor}, Deserialize, }; -use self::{id::IdDeserializer, tag::TagDeserializer}; pub use crate::error::{Error, Position, SpannedError}; use crate::{ error::{Result, SpannedResult}, extensions::Extensions, options::Options, - parse::{AnyNum, Bytes, ParsedStr, BASE64_ENGINE}, + parse::{NewtypeMode, ParsedByteStr, ParsedStr, Parser, StructType, TupleMode}, }; mod id; @@ -22,13 +23,17 @@ mod tag; mod tests; mod value; +const SERDE_CONTENT_CANARY: &str = "serde::__private::de::content::Content"; +const SERDE_TAG_KEY_CANARY: &str = "serde::__private::de::content::TagOrContent"; + /// The RON deserializer. /// /// If you just want to simply deserialize a value, /// you can use the [`from_str`] convenience function. pub struct Deserializer<'de> { - bytes: Bytes<'de>, + pub(crate) parser: Parser<'de>, newtype_variant: bool, + serde_content_newtype: bool, last_identifier: Option<&'de str>, recursion_limit: Option, } @@ -37,36 +42,59 @@ impl<'de> Deserializer<'de> { // Cannot implement trait here since output is tied to input lifetime 'de. #[allow(clippy::should_implement_trait)] pub fn from_str(input: &'de str) -> SpannedResult { - Self::from_str_with_options(input, Options::default()) + Self::from_str_with_options(input, &Options::default()) } pub fn from_bytes(input: &'de [u8]) -> SpannedResult { - Self::from_bytes_with_options(input, Options::default()) + Self::from_bytes_with_options(input, &Options::default()) } - pub fn from_str_with_options(input: &'de str, options: Options) -> SpannedResult { - Self::from_bytes_with_options(input.as_bytes(), options) - } - - pub fn from_bytes_with_options(input: &'de [u8], options: Options) -> SpannedResult { + pub fn from_str_with_options(input: &'de str, options: &Options) -> SpannedResult { let mut deserializer = Deserializer { - bytes: Bytes::new(input)?, + parser: Parser::new(input)?, newtype_variant: false, + serde_content_newtype: false, last_identifier: None, recursion_limit: options.recursion_limit, }; - deserializer.bytes.exts |= options.default_extensions; + deserializer.parser.exts |= options.default_extensions; Ok(deserializer) } - pub fn remainder(&self) -> Cow<'_, str> { - String::from_utf8_lossy(self.bytes.bytes()) + // FIXME: panic is not actually possible, remove once utf8_chunks is stabilized + #[allow(clippy::missing_panics_doc)] + pub fn from_bytes_with_options(input: &'de [u8], options: &Options) -> SpannedResult { + let err = match str::from_utf8(input) { + Ok(input) => return Self::from_str_with_options(input, options), + Err(err) => err, + }; + + // FIXME: use [`utf8_chunks`](https://github.com/rust-lang/rust/issues/99543) once stabilised + #[allow(clippy::expect_used)] + let valid_input = + str::from_utf8(&input[..err.valid_up_to()]).expect("source is valid up to error"); + + Err(SpannedError { + code: err.into(), + position: Position::from_src_end(valid_input), + }) } + #[must_use] + pub fn remainder(&self) -> &'de str { + self.parser.src() + } + + #[must_use] pub fn span_error(&self, code: Error) -> SpannedError { - self.bytes.span_error(code) + self.parser.span_error(code) + } + + #[must_use] + pub fn extensions(&self) -> Extensions { + self.parser.exts } } @@ -122,9 +150,9 @@ impl<'de> Deserializer<'de> { /// Check if the remaining bytes are whitespace only, /// otherwise return an error. pub fn end(&mut self) -> Result<()> { - self.bytes.skip_ws()?; + self.parser.skip_ws()?; - if self.bytes.bytes().is_empty() { + if self.parser.src().is_empty() { Ok(()) } else { Err(Error::TrailingCharacters) @@ -136,25 +164,65 @@ impl<'de> Deserializer<'de> { /// struct and deserializes it accordingly. /// /// This method assumes there is no identifier left. - fn handle_any_struct(&mut self, visitor: V) -> Result + fn handle_any_struct(&mut self, visitor: V, ident: Option<&str>) -> Result where V: Visitor<'de>, { - // Create a working copy - let mut bytes = self.bytes; + // HACK: switch to JSON enum semantics for JSON content + // Robust impl blocked on https://github.com/serde-rs/serde/pull/2420 + let is_serde_content = std::any::type_name::() == SERDE_CONTENT_CANARY + || std::any::type_name::() == SERDE_TAG_KEY_CANARY; - if bytes.consume("(") { - bytes.skip_ws()?; + let old_serde_content_newtype = self.serde_content_newtype; + self.serde_content_newtype = false; - if bytes.check_tuple_struct()? { - // first argument is technically incorrect, but ignored anyway - self.deserialize_tuple(0, visitor) - } else { + match ( + self.parser.check_struct_type( + NewtypeMode::NoParensMeanUnit, + if old_serde_content_newtype { + TupleMode::DifferentiateNewtype // separate match on NewtypeOrTuple below + } else { + TupleMode::ImpreciseTupleOrNewtype // Tuple and NewtypeOrTuple match equally + }, + )?, + ident, + ) { + (StructType::Unit, Some(ident)) if is_serde_content => { + // serde's Content type needs the ident for unit variants + visitor.visit_str(ident) + } + (StructType::Unit, _) => visitor.visit_unit(), + (_, Some(ident)) if is_serde_content => { + // serde's Content type uses a singleton map encoding for enums + visitor.visit_map(SerdeEnumContent { + de: self, + ident: Some(ident), + }) + } + (StructType::Named, _) => { // giving no name results in worse errors but is necessary here self.handle_struct_after_name("", visitor) } - } else { - visitor.visit_unit() + (StructType::NewtypeTuple, _) if old_serde_content_newtype => { + // deserialize a newtype struct or variant + self.parser.consume_char('('); + self.parser.skip_ws()?; + let result = self.deserialize_any(visitor); + self.parser.skip_ws()?; + self.parser.consume_char(')'); + + result + } + ( + StructType::AnyTuple + | StructType::EmptyTuple + | StructType::NewtypeTuple + | StructType::NonNewtypeTuple, + _, + ) => { + // first argument is technically incorrect, but ignored anyway + self.deserialize_tuple(0, visitor) + } } } @@ -173,13 +241,13 @@ impl<'de> Deserializer<'de> { where V: Visitor<'de>, { - if self.newtype_variant || self.bytes.consume("(") { + if self.newtype_variant || self.parser.consume_char('(') { let old_newtype_variant = self.newtype_variant; self.newtype_variant = false; let value = guard_recursion! { self => visitor - .visit_map(CommaSeparated::new(b')', self)) + .visit_map(CommaSeparated::new(Terminator::Struct, self)) .map_err(|err| { struct_error_name( err, @@ -192,9 +260,9 @@ impl<'de> Deserializer<'de> { })? }; - self.bytes.skip_ws()?; + self.parser.skip_ws()?; - if old_newtype_variant || self.bytes.consume(")") { + if old_newtype_variant || self.parser.consume_char(')') { Ok(value) } else { Err(Error::ExpectedStructLikeEnd) @@ -214,65 +282,73 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { where V: Visitor<'de>, { - // Newtype variants can only be unwrapped if we receive information - // about the wrapped type - with `deserialize_any` we don't - self.newtype_variant = false; + if self.newtype_variant { + if self.parser.check_char(')') { + // newtype variant wraps the unit type / a unit struct without name + return self.deserialize_unit(visitor); + } - if self.bytes.consume_ident("true") { + #[allow(clippy::wildcard_in_or_patterns)] + match self + .parser + .check_struct_type(NewtypeMode::InsideNewtype, TupleMode::DifferentiateNewtype)? + { + StructType::Named => { + // newtype variant wraps a named struct + // giving no name results in worse errors but is necessary here + return self.handle_struct_after_name("", visitor); + } + StructType::EmptyTuple | StructType::NonNewtypeTuple => { + // newtype variant wraps a tuple (struct) + // first argument is technically incorrect, but ignored anyway + return self.deserialize_tuple(0, visitor); + } + // StructType::Unit is impossible with NewtypeMode::InsideNewtype + // StructType::AnyTuple is impossible with TupleMode::DifferentiateNewtype + StructType::NewtypeTuple | _ => { + // continue as usual with the inner content of the newtype variant + self.newtype_variant = false; + } + } + } + + if self.parser.consume_ident("true") { return visitor.visit_bool(true); - } else if self.bytes.consume_ident("false") { + } else if self.parser.consume_ident("false") { return visitor.visit_bool(false); - } else if self.bytes.check_ident("Some") { + } else if self.parser.check_ident("Some") { return self.deserialize_option(visitor); - } else if self.bytes.consume_ident("None") { + } else if self.parser.consume_ident("None") { return visitor.visit_none(); - } else if self.bytes.consume("()") { + } else if self.parser.consume_str("()") { return visitor.visit_unit(); - } else if self.bytes.consume_ident("inf") { + } else if self.parser.consume_ident("inf") || self.parser.consume_ident("inff32") { + return visitor.visit_f32(std::f32::INFINITY); + } else if self.parser.consume_ident("inff64") { return visitor.visit_f64(std::f64::INFINITY); - } else if self.bytes.consume_ident("-inf") { - return visitor.visit_f64(std::f64::NEG_INFINITY); - } else if self.bytes.consume_ident("NaN") { + } else if self.parser.consume_ident("NaN") || self.parser.consume_ident("NaNf32") { + return visitor.visit_f32(std::f32::NAN); + } else if self.parser.consume_ident("NaNf64") { return visitor.visit_f64(std::f64::NAN); } - // `identifier` does not change state if it fails - let ident = self.bytes.identifier().ok(); + // `skip_identifier` does not change state if it fails + if let Some(ident) = self.parser.skip_identifier() { + self.parser.skip_ws()?; - if ident.is_some() { - self.bytes.skip_ws()?; - - return self.handle_any_struct(visitor); + return self.handle_any_struct(visitor, Some(ident)); } - match self.bytes.peek_or_eof()? { - b'(' => self.handle_any_struct(visitor), - b'[' => self.deserialize_seq(visitor), - b'{' => self.deserialize_map(visitor), - b'0'..=b'9' | b'+' | b'-' => { - let any_num: AnyNum = self.bytes.any_num()?; - - match any_num { - AnyNum::F32(x) => visitor.visit_f32(x), - AnyNum::F64(x) => visitor.visit_f64(x), - AnyNum::I8(x) => visitor.visit_i8(x), - AnyNum::U8(x) => visitor.visit_u8(x), - AnyNum::I16(x) => visitor.visit_i16(x), - AnyNum::U16(x) => visitor.visit_u16(x), - AnyNum::I32(x) => visitor.visit_i32(x), - AnyNum::U32(x) => visitor.visit_u32(x), - AnyNum::I64(x) => visitor.visit_i64(x), - AnyNum::U64(x) => visitor.visit_u64(x), - #[cfg(feature = "integer128")] - AnyNum::I128(x) => visitor.visit_i128(x), - #[cfg(feature = "integer128")] - AnyNum::U128(x) => visitor.visit_u128(x), - } - } - b'.' => self.deserialize_f64(visitor), - b'"' | b'r' => self.deserialize_string(visitor), - b'\'' => self.deserialize_char(visitor), - other => Err(Error::UnexpectedByte(other as char)), + match self.parser.peek_char_or_eof()? { + '(' => self.handle_any_struct(visitor, None), + '[' => self.deserialize_seq(visitor), + '{' => self.deserialize_map(visitor), + '0'..='9' | '+' | '-' | '.' => self.parser.any_number()?.visit(visitor), + '"' | 'r' => self.deserialize_string(visitor), + 'b' if self.parser.src().starts_with("b'") => self.parser.any_number()?.visit(visitor), + 'b' => self.deserialize_byte_buf(visitor), + '\'' => self.deserialize_char(visitor), + other => Err(Error::UnexpectedChar(other)), } } @@ -280,35 +356,35 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { where V: Visitor<'de>, { - visitor.visit_bool(self.bytes.bool()?) + visitor.visit_bool(self.parser.bool()?) } fn deserialize_i8(self, visitor: V) -> Result where V: Visitor<'de>, { - visitor.visit_i8(self.bytes.signed_integer()?) + visitor.visit_i8(self.parser.integer()?) } fn deserialize_i16(self, visitor: V) -> Result where V: Visitor<'de>, { - visitor.visit_i16(self.bytes.signed_integer()?) + visitor.visit_i16(self.parser.integer()?) } fn deserialize_i32(self, visitor: V) -> Result where V: Visitor<'de>, { - visitor.visit_i32(self.bytes.signed_integer()?) + visitor.visit_i32(self.parser.integer()?) } fn deserialize_i64(self, visitor: V) -> Result where V: Visitor<'de>, { - visitor.visit_i64(self.bytes.signed_integer()?) + visitor.visit_i64(self.parser.integer()?) } #[cfg(feature = "integer128")] @@ -316,35 +392,35 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { where V: Visitor<'de>, { - visitor.visit_i128(self.bytes.signed_integer()?) + visitor.visit_i128(self.parser.integer()?) } fn deserialize_u8(self, visitor: V) -> Result where V: Visitor<'de>, { - visitor.visit_u8(self.bytes.unsigned_integer()?) + visitor.visit_u8(self.parser.integer()?) } fn deserialize_u16(self, visitor: V) -> Result where V: Visitor<'de>, { - visitor.visit_u16(self.bytes.unsigned_integer()?) + visitor.visit_u16(self.parser.integer()?) } fn deserialize_u32(self, visitor: V) -> Result where V: Visitor<'de>, { - visitor.visit_u32(self.bytes.unsigned_integer()?) + visitor.visit_u32(self.parser.integer()?) } fn deserialize_u64(self, visitor: V) -> Result where V: Visitor<'de>, { - visitor.visit_u64(self.bytes.unsigned_integer()?) + visitor.visit_u64(self.parser.integer()?) } #[cfg(feature = "integer128")] @@ -352,35 +428,35 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { where V: Visitor<'de>, { - visitor.visit_u128(self.bytes.unsigned_integer()?) + visitor.visit_u128(self.parser.integer()?) } fn deserialize_f32(self, visitor: V) -> Result where V: Visitor<'de>, { - visitor.visit_f32(self.bytes.float()?) + visitor.visit_f32(self.parser.float()?) } fn deserialize_f64(self, visitor: V) -> Result where V: Visitor<'de>, { - visitor.visit_f64(self.bytes.float()?) + visitor.visit_f64(self.parser.float()?) } fn deserialize_char(self, visitor: V) -> Result where V: Visitor<'de>, { - visitor.visit_char(self.bytes.char()?) + visitor.visit_char(self.parser.char()?) } fn deserialize_str(self, visitor: V) -> Result where V: Visitor<'de>, { - match self.bytes.string()? { + match self.parser.string()? { ParsedStr::Allocated(s) => visitor.visit_string(s), ParsedStr::Slice(s) => visitor.visit_borrowed_str(s), } @@ -404,23 +480,14 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { where V: Visitor<'de>, { - if let Some(b'[') = self.bytes.peek() { + if self.parser.check_char('[') { let bytes = Vec::::deserialize(self)?; return visitor.visit_byte_buf(bytes); } - let res = { - let string = self.bytes.string()?; - let base64_str = match string { - ParsedStr::Allocated(ref s) => s.as_str(), - ParsedStr::Slice(s) => s, - }; - BASE64_ENGINE.decode(base64_str) - }; - - match res { - Ok(byte_buf) => visitor.visit_byte_buf(byte_buf), - Err(err) => Err(Error::Base64Error(err)), + match self.parser.byte_string()? { + ParsedByteStr::Allocated(byte_buf) => visitor.visit_byte_buf(byte_buf), + ParsedByteStr::Slice(bytes) => visitor.visit_borrowed_bytes(bytes), } } @@ -428,24 +495,31 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { where V: Visitor<'de>, { - if self.bytes.consume("None") { + if self.parser.consume_ident("None") { visitor.visit_none() - } else if self.bytes.consume("Some") && { - self.bytes.skip_ws()?; - self.bytes.consume("(") + } else if self.parser.consume_ident("Some") && { + self.parser.skip_ws()?; + self.parser.consume_char('(') } { - self.bytes.skip_ws()?; + self.parser.skip_ws()?; + + self.newtype_variant = self + .parser + .exts + .contains(Extensions::UNWRAP_VARIANT_NEWTYPES); let v = guard_recursion! { self => visitor.visit_some(&mut *self)? }; - self.bytes.comma()?; + self.newtype_variant = false; - if self.bytes.consume(")") { + self.parser.comma()?; + + if self.parser.consume_char(')') { Ok(v) } else { Err(Error::ExpectedOptionEnd) } - } else if self.bytes.exts.contains(Extensions::IMPLICIT_SOME) { + } else if self.parser.exts.contains(Extensions::IMPLICIT_SOME) { guard_recursion! { self => visitor.visit_some(&mut *self) } } else { Err(Error::ExpectedOption) @@ -457,7 +531,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { where V: Visitor<'de>, { - if self.newtype_variant || self.bytes.consume("()") { + if self.newtype_variant || self.parser.consume_str("()") { self.newtype_variant = false; visitor.visit_unit() @@ -470,7 +544,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { where V: Visitor<'de>, { - if self.newtype_variant || self.bytes.consume_struct_name(name)? { + if self.newtype_variant || self.parser.consume_struct_name(name)? { self.newtype_variant = false; visitor.visit_unit() @@ -483,22 +557,40 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { where V: Visitor<'de>, { - if self.bytes.exts.contains(Extensions::UNWRAP_NEWTYPES) || self.newtype_variant { + if name == crate::value::raw::RAW_VALUE_TOKEN { + let src_before = self.parser.pre_ws_src(); + self.parser.skip_ws()?; + let _ignored = self.deserialize_ignored_any(serde::de::IgnoredAny)?; + self.parser.skip_ws()?; + let src_after = self.parser.src(); + + if self.parser.has_unclosed_line_comment() { + return Err(Error::UnclosedLineComment); + } + + let ron_str = &src_before[..src_before.len() - src_after.len()]; + + return visitor + .visit_borrowed_str::(ron_str) + .map_err(|_| Error::ExpectedRawValue); + } + + if self.parser.exts.contains(Extensions::UNWRAP_NEWTYPES) || self.newtype_variant { self.newtype_variant = false; return guard_recursion! { self => visitor.visit_newtype_struct(&mut *self) }; } - self.bytes.consume_struct_name(name)?; + self.parser.consume_struct_name(name)?; - self.bytes.skip_ws()?; + self.parser.skip_ws()?; - if self.bytes.consume("(") { - self.bytes.skip_ws()?; + if self.parser.consume_char('(') { + self.parser.skip_ws()?; let value = guard_recursion! { self => visitor.visit_newtype_struct(&mut *self)? }; - self.bytes.comma()?; + self.parser.comma()?; - if self.bytes.consume(")") { + if self.parser.consume_char(')') { Ok(value) } else { Err(Error::ExpectedStructLikeEnd) @@ -516,13 +608,13 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { { self.newtype_variant = false; - if self.bytes.consume("[") { + if self.parser.consume_char('[') { let value = guard_recursion! { self => - visitor.visit_seq(CommaSeparated::new(b']', self))? + visitor.visit_seq(CommaSeparated::new(Terminator::Seq, self))? }; - self.bytes.skip_ws()?; + self.parser.skip_ws()?; - if self.bytes.consume("]") { + if self.parser.consume_char(']') { Ok(value) } else { Err(Error::ExpectedArrayEnd) @@ -536,16 +628,16 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { where V: Visitor<'de>, { - if self.newtype_variant || self.bytes.consume("(") { + if self.newtype_variant || self.parser.consume_char('(') { let old_newtype_variant = self.newtype_variant; self.newtype_variant = false; let value = guard_recursion! { self => - visitor.visit_seq(CommaSeparated::new(b')', self))? + visitor.visit_seq(CommaSeparated::new(Terminator::Tuple, self))? }; - self.bytes.skip_ws()?; + self.parser.skip_ws()?; - if old_newtype_variant || self.bytes.consume(")") { + if old_newtype_variant || self.parser.consume_char(')') { Ok(value) } else { Err(Error::ExpectedStructLikeEnd) @@ -565,7 +657,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { V: Visitor<'de>, { if !self.newtype_variant { - self.bytes.consume_struct_name(name)?; + self.parser.consume_struct_name(name)?; } self.deserialize_tuple(len, visitor).map_err(|e| match e { @@ -578,15 +670,37 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { where V: Visitor<'de>, { + // Detect `#[serde(flatten)]` as a struct deserialised as a map + const SERDE_FLATTEN_CANARY: &[u8] = b"struct "; + + struct VisitorExpecting(V); + impl<'de, V: Visitor<'de>> std::fmt::Display for VisitorExpecting<&'_ V> { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + self.0.expecting(fmt) + } + } + self.newtype_variant = false; - if self.bytes.consume("{") { - let value = guard_recursion! { self => - visitor.visit_map(CommaSeparated::new(b'}', self))? - }; - self.bytes.skip_ws()?; + let mut canary_buffer = [0u8; SERDE_FLATTEN_CANARY.len()]; + std::mem::drop(write!( + canary_buffer.as_mut(), + "{}", + VisitorExpecting(&visitor) + )); + let terminator = if canary_buffer == SERDE_FLATTEN_CANARY { + Terminator::MapAsStruct + } else { + Terminator::Map + }; - if self.bytes.consume("}") { + if self.parser.consume_char('{') { + let value = guard_recursion! { self => + visitor.visit_map(CommaSeparated::new(terminator, self))? + }; + self.parser.skip_ws()?; + + if self.parser.consume_char('}') { Ok(value) } else { Err(Error::ExpectedMapEnd) @@ -606,10 +720,10 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { V: Visitor<'de>, { if !self.newtype_variant { - self.bytes.consume_struct_name(name)?; + self.parser.consume_struct_name(name)?; } - self.bytes.skip_ws()?; + self.parser.skip_ws()?; self.handle_struct_after_name(name, visitor) } @@ -644,7 +758,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { where V: Visitor<'de>, { - let identifier = str::from_utf8(self.bytes.identifier()?).map_err(Error::from)?; + let identifier = self.parser.identifier()?; self.last_identifier = Some(identifier); @@ -659,27 +773,47 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut Deserializer<'de> { } } +enum Terminator { + Map, + MapAsStruct, + Tuple, + Struct, + Seq, +} + +impl Terminator { + fn as_char(&self) -> char { + match self { + Terminator::Map | Terminator::MapAsStruct => '}', + Terminator::Tuple | Terminator::Struct => ')', + Terminator::Seq => ']', + } + } +} + struct CommaSeparated<'a, 'de: 'a> { de: &'a mut Deserializer<'de>, - terminator: u8, + terminator: Terminator, had_comma: bool, + inside_internally_tagged_enum: bool, } impl<'a, 'de> CommaSeparated<'a, 'de> { - fn new(terminator: u8, de: &'a mut Deserializer<'de>) -> Self { + fn new(terminator: Terminator, de: &'a mut Deserializer<'de>) -> Self { CommaSeparated { de, terminator, had_comma: true, + inside_internally_tagged_enum: false, } } fn has_element(&mut self) -> Result { - self.de.bytes.skip_ws()?; + self.de.parser.skip_ws()?; match ( self.had_comma, - self.de.bytes.peek_or_eof()? != self.terminator, + !self.de.parser.check_char(self.terminator.as_char()), ) { // Trailing comma, maybe has a next element (true, has_element) => Ok(has_element), @@ -701,7 +835,7 @@ impl<'de, 'a> de::SeqAccess<'de> for CommaSeparated<'a, 'de> { if self.has_element()? { let res = guard_recursion! { self.de => seed.deserialize(&mut *self.de)? }; - self.had_comma = self.de.bytes.comma()?; + self.had_comma = self.de.parser.comma()?; Ok(Some(res)) } else { @@ -718,12 +852,17 @@ impl<'de, 'a> de::MapAccess<'de> for CommaSeparated<'a, 'de> { K: DeserializeSeed<'de>, { if self.has_element()? { - if self.terminator == b')' { - guard_recursion! { self.de => - seed.deserialize(&mut IdDeserializer::new(&mut *self.de)).map(Some) - } - } else { - guard_recursion! { self.de => seed.deserialize(&mut *self.de).map(Some) } + self.inside_internally_tagged_enum = + std::any::type_name::() == SERDE_TAG_KEY_CANARY; + + match self.terminator { + Terminator::Struct => guard_recursion! { self.de => + seed.deserialize(&mut id::Deserializer::new(&mut *self.de, false)).map(Some) + }, + Terminator::MapAsStruct => guard_recursion! { self.de => + seed.deserialize(&mut id::Deserializer::new(&mut *self.de, true)).map(Some) + }, + _ => guard_recursion! { self.de => seed.deserialize(&mut *self.de).map(Some) }, } } else { Ok(None) @@ -734,16 +873,24 @@ impl<'de, 'a> de::MapAccess<'de> for CommaSeparated<'a, 'de> { where V: DeserializeSeed<'de>, { - self.de.bytes.skip_ws()?; + self.de.parser.skip_ws()?; - if self.de.bytes.consume(":") { - self.de.bytes.skip_ws()?; + if self.de.parser.consume_char(':') { + self.de.parser.skip_ws()?; - let res = guard_recursion! { self.de => - seed.deserialize(&mut TagDeserializer::new(&mut *self.de))? + let res = if self.inside_internally_tagged_enum + && std::any::type_name::() != SERDE_CONTENT_CANARY + { + guard_recursion! { self.de => + seed.deserialize(&mut tag::Deserializer::new(&mut *self.de))? + } + } else { + guard_recursion! { self.de => + seed.deserialize(&mut *self.de)? + } }; - self.had_comma = self.de.bytes.comma()?; + self.had_comma = self.de.parser.comma()?; Ok(res) } else { @@ -770,7 +917,7 @@ impl<'de, 'a> de::EnumAccess<'de> for Enum<'a, 'de> { where V: DeserializeSeed<'de>, { - self.de.bytes.skip_ws()?; + self.de.parser.skip_ws()?; let value = guard_recursion! { self.de => seed.deserialize(&mut *self.de)? }; @@ -791,14 +938,14 @@ impl<'de, 'a> de::VariantAccess<'de> for Enum<'a, 'de> { { let newtype_variant = self.de.last_identifier; - self.de.bytes.skip_ws()?; + self.de.parser.skip_ws()?; - if self.de.bytes.consume("(") { - self.de.bytes.skip_ws()?; + if self.de.parser.consume_char('(') { + self.de.parser.skip_ws()?; self.de.newtype_variant = self .de - .bytes + .parser .exts .contains(Extensions::UNWRAP_VARIANT_NEWTYPES); @@ -810,9 +957,9 @@ impl<'de, 'a> de::VariantAccess<'de> for Enum<'a, 'de> { self.de.newtype_variant = false; - self.de.bytes.comma()?; + self.de.parser.comma()?; - if self.de.bytes.consume(")") { + if self.de.parser.consume_char(')') { Ok(val) } else { Err(Error::ExpectedStructLikeEnd) @@ -826,7 +973,7 @@ impl<'de, 'a> de::VariantAccess<'de> for Enum<'a, 'de> { where V: Visitor<'de>, { - self.de.bytes.skip_ws()?; + self.de.parser.skip_ws()?; self.de.deserialize_tuple(len, visitor) } @@ -837,7 +984,7 @@ impl<'de, 'a> de::VariantAccess<'de> for Enum<'a, 'de> { { let struct_variant = self.de.last_identifier; - self.de.bytes.skip_ws()?; + self.de.parser.skip_ws()?; self.de .handle_struct_after_name("", visitor) @@ -867,3 +1014,36 @@ fn struct_error_name(error: Error, name: Option<&str>) -> Error { e => e, } } + +struct SerdeEnumContent<'a, 'de: 'a> { + de: &'a mut Deserializer<'de>, + ident: Option<&'a str>, +} + +impl<'de, 'a> de::MapAccess<'de> for SerdeEnumContent<'a, 'de> { + type Error = Error; + + fn next_key_seed(&mut self, seed: K) -> Result> + where + K: DeserializeSeed<'de>, + { + self.ident + .take() + .map(|ident| seed.deserialize(serde::de::value::StrDeserializer::new(ident))) + .transpose() + } + + fn next_value_seed(&mut self, seed: V) -> Result + where + V: DeserializeSeed<'de>, + { + self.de.parser.skip_ws()?; + + let old_serde_content_newtype = self.de.serde_content_newtype; + self.de.serde_content_newtype = true; + let result = seed.deserialize(&mut *self.de); + self.de.serde_content_newtype = old_serde_content_newtype; + + result + } +} diff --git a/third_party/rust/ron/src/de/tag.rs b/third_party/rust/ron/src/de/tag.rs index 9e204ff847c6..af3305ce560e 100644 --- a/third_party/rust/ron/src/de/tag.rs +++ b/third_party/rust/ron/src/de/tag.rs @@ -1,25 +1,32 @@ use serde::de::{self, Visitor}; -use super::{Deserializer, Error, Result}; +use super::{Error, Result}; -pub struct TagDeserializer<'a, 'b: 'a> { - d: &'a mut Deserializer<'b>, +pub struct Deserializer<'a, 'b: 'a> { + de: &'a mut super::Deserializer<'b>, } -impl<'a, 'b: 'a> TagDeserializer<'a, 'b> { - pub fn new(d: &'a mut Deserializer<'b>) -> Self { - TagDeserializer { d } +impl<'a, 'b: 'a> Deserializer<'a, 'b> { + pub fn new(de: &'a mut super::Deserializer<'b>) -> Self { + Deserializer { de } } } -impl<'a, 'b: 'a, 'c> de::Deserializer<'b> for &'c mut TagDeserializer<'a, 'b> { +impl<'a, 'b: 'a, 'c> de::Deserializer<'b> for &'c mut Deserializer<'a, 'b> { type Error = Error; fn deserialize_str(self, visitor: V) -> Result where V: Visitor<'b>, { - self.d.deserialize_str(visitor) + self.de.deserialize_str(visitor) + } + + fn deserialize_string(self, visitor: V) -> std::result::Result + where + V: Visitor<'b>, + { + self.deserialize_str(visitor) } fn deserialize_identifier(self, visitor: V) -> Result @@ -33,219 +40,212 @@ impl<'a, 'b: 'a, 'c> de::Deserializer<'b> for &'c mut TagDeserializer<'a, 'b> { where V: Visitor<'b>, { - self.d.deserialize_any(visitor) + self.deserialize_str(visitor) } - fn deserialize_bool(self, visitor: V) -> Result + fn deserialize_bool(self, _visitor: V) -> Result where V: Visitor<'b>, { - self.d.deserialize_bool(visitor) + Err(Error::ExpectedString) } - fn deserialize_i8(self, visitor: V) -> Result + fn deserialize_i8(self, _visitor: V) -> Result where V: Visitor<'b>, { - self.d.deserialize_i8(visitor) + Err(Error::ExpectedString) } - fn deserialize_i16(self, visitor: V) -> Result + fn deserialize_i16(self, _visitor: V) -> Result where V: Visitor<'b>, { - self.d.deserialize_i16(visitor) + Err(Error::ExpectedString) } - fn deserialize_i32(self, visitor: V) -> Result + fn deserialize_i32(self, _visitor: V) -> Result where V: Visitor<'b>, { - self.d.deserialize_i32(visitor) + Err(Error::ExpectedString) } - fn deserialize_i64(self, visitor: V) -> Result + fn deserialize_i64(self, _visitor: V) -> Result where V: Visitor<'b>, { - self.d.deserialize_i64(visitor) + Err(Error::ExpectedString) } #[cfg(feature = "integer128")] - fn deserialize_i128(self, visitor: V) -> Result + fn deserialize_i128(self, _visitor: V) -> Result where V: Visitor<'b>, { - self.d.deserialize_i128(visitor) + Err(Error::ExpectedString) } - fn deserialize_u8(self, visitor: V) -> Result + fn deserialize_u8(self, _visitor: V) -> Result where V: Visitor<'b>, { - self.d.deserialize_u8(visitor) + Err(Error::ExpectedString) } - fn deserialize_u16(self, visitor: V) -> Result + fn deserialize_u16(self, _visitor: V) -> Result where V: Visitor<'b>, { - self.d.deserialize_u16(visitor) + Err(Error::ExpectedString) } - fn deserialize_u32(self, visitor: V) -> Result + fn deserialize_u32(self, _visitor: V) -> Result where V: Visitor<'b>, { - self.d.deserialize_u32(visitor) + Err(Error::ExpectedString) } - fn deserialize_u64(self, visitor: V) -> Result + fn deserialize_u64(self, _visitor: V) -> Result where V: Visitor<'b>, { - self.d.deserialize_u64(visitor) + Err(Error::ExpectedString) } #[cfg(feature = "integer128")] - fn deserialize_u128(self, visitor: V) -> Result + fn deserialize_u128(self, _visitor: V) -> Result where V: Visitor<'b>, { - self.d.deserialize_u128(visitor) + Err(Error::ExpectedString) } - fn deserialize_f32(self, visitor: V) -> Result + fn deserialize_f32(self, _visitor: V) -> Result where V: Visitor<'b>, { - self.d.deserialize_f32(visitor) + Err(Error::ExpectedString) } - fn deserialize_f64(self, visitor: V) -> Result + fn deserialize_f64(self, _visitor: V) -> Result where V: Visitor<'b>, { - self.d.deserialize_f64(visitor) + Err(Error::ExpectedString) } - fn deserialize_char(self, visitor: V) -> Result + fn deserialize_char(self, _visitor: V) -> Result where V: Visitor<'b>, { - self.d.deserialize_char(visitor) + Err(Error::ExpectedString) } - fn deserialize_string(self, visitor: V) -> Result + fn deserialize_bytes(self, _visitor: V) -> Result where V: Visitor<'b>, { - self.d.deserialize_string(visitor) + Err(Error::ExpectedString) } - fn deserialize_bytes(self, visitor: V) -> Result + fn deserialize_byte_buf(self, _visitor: V) -> Result where V: Visitor<'b>, { - self.d.deserialize_bytes(visitor) + Err(Error::ExpectedString) } - fn deserialize_byte_buf(self, visitor: V) -> Result + fn deserialize_option(self, _visitor: V) -> Result where V: Visitor<'b>, { - self.d.deserialize_byte_buf(visitor) + Err(Error::ExpectedString) } - fn deserialize_option(self, visitor: V) -> Result + fn deserialize_unit(self, _visitor: V) -> Result where V: Visitor<'b>, { - self.d.deserialize_option(visitor) + Err(Error::ExpectedString) } - fn deserialize_unit(self, visitor: V) -> Result + fn deserialize_unit_struct(self, _name: &'static str, _visitor: V) -> Result where V: Visitor<'b>, { - self.d.deserialize_unit(visitor) + Err(Error::ExpectedString) } - fn deserialize_unit_struct(self, name: &'static str, visitor: V) -> Result + fn deserialize_newtype_struct(self, _name: &'static str, _visitor: V) -> Result where V: Visitor<'b>, { - self.d.deserialize_unit_struct(name, visitor) + Err(Error::ExpectedString) } - fn deserialize_newtype_struct(self, name: &'static str, visitor: V) -> Result + fn deserialize_seq(self, _visitor: V) -> Result where V: Visitor<'b>, { - self.d.deserialize_newtype_struct(name, visitor) + Err(Error::ExpectedString) } - fn deserialize_seq(self, visitor: V) -> Result + fn deserialize_tuple(self, _len: usize, _visitor: V) -> Result where V: Visitor<'b>, { - self.d.deserialize_seq(visitor) - } - - fn deserialize_tuple(self, len: usize, visitor: V) -> Result - where - V: Visitor<'b>, - { - self.d.deserialize_tuple(len, visitor) + Err(Error::ExpectedString) } fn deserialize_tuple_struct( self, - name: &'static str, - len: usize, - visitor: V, + _name: &'static str, + _len: usize, + _visitor: V, ) -> Result where V: Visitor<'b>, { - self.d.deserialize_tuple_struct(name, len, visitor) + Err(Error::ExpectedString) } - fn deserialize_map(self, visitor: V) -> Result + fn deserialize_map(self, _visitor: V) -> Result where V: Visitor<'b>, { - self.d.deserialize_map(visitor) + Err(Error::ExpectedString) } fn deserialize_struct( self, - name: &'static str, - fields: &'static [&'static str], - visitor: V, + _name: &'static str, + _fields: &'static [&'static str], + _visitor: V, ) -> Result where V: Visitor<'b>, { - self.d.deserialize_struct(name, fields, visitor) + Err(Error::ExpectedString) } fn deserialize_enum( self, - name: &'static str, - variants: &'static [&'static str], - visitor: V, + _name: &'static str, + _variants: &'static [&'static str], + _visitor: V, ) -> Result where V: Visitor<'b>, { - self.d.deserialize_enum(name, variants, visitor) + Err(Error::ExpectedString) } fn deserialize_ignored_any(self, visitor: V) -> Result where V: Visitor<'b>, { - self.d.deserialize_ignored_any(visitor) + self.deserialize_any(visitor) } } diff --git a/third_party/rust/ron/src/de/tests.rs b/third_party/rust/ron/src/de/tests.rs index 2c46eb68f967..d814fee65ee1 100644 --- a/third_party/rust/ron/src/de/tests.rs +++ b/third_party/rust/ron/src/de/tests.rs @@ -2,9 +2,9 @@ use serde_bytes; use serde_derive::Deserialize; use crate::{ - de::from_str, error::{Error, Position, SpannedError, SpannedResult}, - parse::{AnyNum, Bytes}, + parse::Parser, + value::Number, }; #[derive(Debug, PartialEq, Deserialize)] @@ -13,6 +13,20 @@ struct EmptyStruct1; #[derive(Debug, PartialEq, Deserialize)] struct EmptyStruct2 {} +#[derive(Debug, PartialEq, Deserialize)] +struct NewType(i32); + +#[derive(Debug, PartialEq, Deserialize)] +#[serde(rename = "")] +struct UnnamedNewType(i32); + +#[derive(Debug, PartialEq, Deserialize)] +struct TupleStruct(f32, f32); + +#[derive(Debug, PartialEq, Deserialize)] +#[serde(rename = "")] +struct UnnamedTupleStruct(f32, f32); + #[derive(Clone, Copy, Debug, PartialEq, Deserialize)] struct MyStruct { x: f32, @@ -36,53 +50,230 @@ struct BytesStruct { #[test] fn test_empty_struct() { - assert_eq!(Ok(EmptyStruct1), from_str("EmptyStruct1")); - assert_eq!(Ok(EmptyStruct2 {}), from_str("EmptyStruct2()")); + check_from_str_bytes_reader("EmptyStruct1", Ok(EmptyStruct1)); + check_from_str_bytes_reader("EmptyStruct2()", Ok(EmptyStruct2 {})); } #[test] fn test_struct() { let my_struct = MyStruct { x: 4.0, y: 7.0 }; - assert_eq!(Ok(my_struct), from_str("MyStruct(x:4,y:7,)")); - assert_eq!(Ok(my_struct), from_str("(x:4,y:7)")); + check_from_str_bytes_reader("MyStruct(x:4,y:7,)", Ok(my_struct)); + check_from_str_bytes_reader("(x:4,y:7)", Ok(my_struct)); - #[derive(Debug, PartialEq, Deserialize)] - struct NewType(i32); + check_from_str_bytes_reader("NewType(42)", Ok(NewType(42))); + check_from_str_bytes_reader("(33)", Ok(NewType(33))); - assert_eq!(Ok(NewType(42)), from_str("NewType(42)")); - assert_eq!(Ok(NewType(33)), from_str("(33)")); + check_from_str_bytes_reader::( + "NewType", + Err(SpannedError { + code: Error::ExpectedNamedStructLike("NewType"), + position: Position { line: 1, col: 8 }, + }), + ); + check_from_str_bytes_reader::( + "", + Err(SpannedError { + code: Error::ExpectedStructLike, + position: Position { line: 1, col: 1 }, + }), + ); + check_from_str_bytes_reader("(33)", Ok(UnnamedNewType(33))); + check_from_str_bytes_reader::( + "Newtype", + Err(SpannedError { + code: Error::ExpectedNamedStructLike(""), + position: Position { line: 1, col: 8 }, + }), + ); - #[derive(Debug, PartialEq, Deserialize)] - struct TupleStruct(f32, f32); + check_from_str_bytes_reader("TupleStruct(2,5,)", Ok(TupleStruct(2.0, 5.0))); + check_from_str_bytes_reader("(3,4)", Ok(TupleStruct(3.0, 4.0))); + check_from_str_bytes_reader::( + "", + Err(SpannedError { + code: Error::ExpectedNamedStructLike("TupleStruct"), + position: Position { line: 1, col: 1 }, + }), + ); + check_from_str_bytes_reader::( + "TupleStruct(2,5,)", + Err(SpannedError { + code: Error::ExpectedNamedStructLike(""), + position: Position { line: 1, col: 12 }, + }), + ); + check_from_str_bytes_reader("(3,4)", Ok(UnnamedTupleStruct(3.0, 4.0))); + check_from_str_bytes_reader::( + "", + Err(SpannedError { + code: Error::ExpectedStructLike, + position: Position { line: 1, col: 1 }, + }), + ); +} - assert_eq!(Ok(TupleStruct(2.0, 5.0)), from_str("TupleStruct(2,5,)")); - assert_eq!(Ok(TupleStruct(3.0, 4.0)), from_str("(3,4)")); +#[test] +fn test_unclosed_limited_seq_struct() { + #[derive(Debug, PartialEq)] + struct LimitedStruct; + + impl<'de> serde::Deserialize<'de> for LimitedStruct { + fn deserialize>(deserializer: D) -> Result { + struct Visitor; + + impl<'de> serde::de::Visitor<'de> for Visitor { + type Value = LimitedStruct; + + // GRCOV_EXCL_START + fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.write_str("struct LimitedStruct") + } + // GRCOV_EXCL_STOP + + fn visit_map>( + self, + _map: A, + ) -> Result { + Ok(LimitedStruct) + } + } + + deserializer.deserialize_struct("LimitedStruct", &[], Visitor) + } + } + + check_from_str_bytes_reader::( + "(", + Err(SpannedError { + code: Error::ExpectedStructLikeEnd, + position: Position { line: 1, col: 2 }, + }), + ) +} + +#[test] +fn test_unclosed_limited_seq() { + #[derive(Debug, PartialEq)] + struct LimitedSeq; + + impl<'de> serde::Deserialize<'de> for LimitedSeq { + fn deserialize>(deserializer: D) -> Result { + struct Visitor; + + impl<'de> serde::de::Visitor<'de> for Visitor { + type Value = LimitedSeq; + + // GRCOV_EXCL_START + fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.write_str("an empty sequence") + } + // GRCOV_EXCL_STOP + + fn visit_seq>( + self, + _seq: A, + ) -> Result { + Ok(LimitedSeq) + } + } + + deserializer.deserialize_seq(Visitor) + } + } + + check_from_str_bytes_reader::( + "[", + Err(SpannedError { + code: Error::ExpectedArrayEnd, + position: Position { line: 1, col: 2 }, + }), + ); + + assert_eq!( + crate::Value::from(vec![42]).into_rust::(), + Err(Error::ExpectedDifferentLength { + expected: String::from("a sequence of length 0"), + found: 1 + }) + ); +} + +#[test] +fn test_unclosed_limited_map() { + #[derive(Debug, PartialEq)] + struct LimitedMap; + + impl<'de> serde::Deserialize<'de> for LimitedMap { + fn deserialize>(deserializer: D) -> Result { + struct Visitor; + + impl<'de> serde::de::Visitor<'de> for Visitor { + type Value = LimitedMap; + + // GRCOV_EXCL_START + fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.write_str("an empty map") + } + // GRCOV_EXCL_STOP + + fn visit_map>( + self, + _map: A, + ) -> Result { + Ok(LimitedMap) + } + } + + deserializer.deserialize_map(Visitor) + } + } + + check_from_str_bytes_reader::( + "{", + Err(SpannedError { + code: Error::ExpectedMapEnd, + position: Position { line: 1, col: 2 }, + }), + ); + + assert_eq!( + crate::Value::Map([("a", 42)].into_iter().collect()).into_rust::(), + Err(Error::ExpectedDifferentLength { + expected: String::from("a map of length 0"), + found: 1 + }) + ); } #[test] fn test_option() { - assert_eq!(Ok(Some(1u8)), from_str("Some(1)")); - assert_eq!(Ok(None::), from_str("None")); + check_from_str_bytes_reader("Some(1)", Ok(Some(1u8))); + check_from_str_bytes_reader("None", Ok(None::)); } #[test] fn test_enum() { - assert_eq!(Ok(MyEnum::A), from_str("A")); - assert_eq!(Ok(MyEnum::B(true)), from_str("B(true,)")); - assert_eq!(Ok(MyEnum::C(true, 3.5)), from_str("C(true,3.5,)")); - assert_eq!(Ok(MyEnum::D { a: 2, b: 3 }), from_str("D(a:2,b:3,)")); + check_from_str_bytes_reader("A", Ok(MyEnum::A)); + check_from_str_bytes_reader("B(true,)", Ok(MyEnum::B(true))); + check_from_str_bytes_reader::( + "B", + Err(SpannedError { + code: Error::ExpectedStructLike, + position: Position { line: 1, col: 2 }, + }), + ); + check_from_str_bytes_reader("C(true,3.5,)", Ok(MyEnum::C(true, 3.5))); + check_from_str_bytes_reader("D(a:2,b:3,)", Ok(MyEnum::D { a: 2, b: 3 })); } #[test] fn test_array() { - let empty: [i32; 0] = []; - assert_eq!(Ok(empty), from_str("()")); - let empty_array = empty.to_vec(); - assert_eq!(Ok(empty_array), from_str("[]")); + check_from_str_bytes_reader::<[i32; 0]>("()", Ok([])); + check_from_str_bytes_reader("[]", Ok(Vec::::new())); - assert_eq!(Ok([2, 3, 4i32]), from_str("(2,3,4,)")); - assert_eq!(Ok([2, 3, 4i32].to_vec()), from_str("[2,3,4,]")); + check_from_str_bytes_reader("(2,3,4,)", Ok([2, 3, 4i32])); + check_from_str_bytes_reader("[2,3,4,]", Ok([2, 3, 4i32].to_vec())); } #[test] @@ -93,63 +284,58 @@ fn test_map() { map.insert((true, false), 4); map.insert((false, false), 123); - assert_eq!( - Ok(map), - from_str( - "{ + check_from_str_bytes_reader( + "{ (true,false,):4, (false,false,):123, - }" - ) + }", + Ok(map), ); } #[test] fn test_string() { - let s: String = from_str("\"String\"").unwrap(); - assert_eq!("String", s); + check_from_str_bytes_reader("\"String\"", Ok(String::from("String"))); - let raw: String = from_str("r\"String\"").unwrap(); - assert_eq!("String", raw); + check_from_str_bytes_reader("r\"String\"", Ok(String::from("String"))); + check_from_str_bytes_reader("r#\"String\"#", Ok(String::from("String"))); - let raw_hashes: String = from_str("r#\"String\"#").unwrap(); - assert_eq!("String", raw_hashes); + check_from_str_bytes_reader( + "r#\"String with\nmultiple\nlines\n\"#", + Ok(String::from("String with\nmultiple\nlines\n")), + ); - let raw_hashes_multiline: String = from_str("r#\"String with\nmultiple\nlines\n\"#").unwrap(); - assert_eq!("String with\nmultiple\nlines\n", raw_hashes_multiline); - - let raw_hashes_quote: String = from_str("r##\"String with \"#\"##").unwrap(); - assert_eq!("String with \"#", raw_hashes_quote); + check_from_str_bytes_reader( + "r##\"String with \"#\"##", + Ok(String::from("String with \"#")), + ); } #[test] fn test_char() { - assert_eq!(Ok('c'), from_str("'c'")); + check_from_str_bytes_reader("'c'", Ok('c')); } #[test] fn test_escape_char() { - assert_eq!('\'', from_str::("'\\''").unwrap()); + check_from_str_bytes_reader("'\\''", Ok('\'')); } #[test] fn test_escape() { - assert_eq!("\"Quoted\"", from_str::(r#""\"Quoted\"""#).unwrap()); + check_from_str_bytes_reader(r#""\"Quoted\"""#, Ok(String::from("\"Quoted\""))); } #[test] fn test_comment() { - assert_eq!( - MyStruct { x: 1.0, y: 2.0 }, - from_str( - "( + check_from_str_bytes_reader( + "( x: 1.0, // x is just 1 // There is another comment in the very next line.. // And y is indeed y: 2.0 // 2! - )" - ) - .unwrap() + )", + Ok(MyStruct { x: 1.0, y: 2.0 }), ); } @@ -164,47 +350,45 @@ fn err(kind: Error, line: usize, col: usize) -> SpannedResult { fn test_err_wrong_value() { use std::collections::HashMap; - use self::Error::*; - - assert_eq!(from_str::("'c'"), err(ExpectedFloat, 1, 1)); - assert_eq!(from_str::("'c'"), err(ExpectedString, 1, 1)); - assert_eq!(from_str::>("'c'"), err(ExpectedMap, 1, 1)); - assert_eq!(from_str::<[u8; 5]>("'c'"), err(ExpectedStructLike, 1, 1)); - assert_eq!(from_str::>("'c'"), err(ExpectedArray, 1, 1)); - assert_eq!(from_str::("'c'"), err(ExpectedIdentifier, 1, 1)); - assert_eq!( - from_str::("'c'"), - err(ExpectedNamedStructLike("MyStruct"), 1, 1) + check_from_str_bytes_reader::("'c'", err(Error::ExpectedFloat, 1, 1)); + check_from_str_bytes_reader::("'c'", err(Error::ExpectedString, 1, 1)); + check_from_str_bytes_reader::>("'c'", err(Error::ExpectedMap, 1, 1)); + check_from_str_bytes_reader::<[u8; 5]>("'c'", err(Error::ExpectedStructLike, 1, 1)); + check_from_str_bytes_reader::>("'c'", err(Error::ExpectedArray, 1, 1)); + check_from_str_bytes_reader::("'c'", err(Error::ExpectedIdentifier, 1, 1)); + check_from_str_bytes_reader::( + "'c'", + err(Error::ExpectedNamedStructLike("MyStruct"), 1, 1), ); - assert_eq!( - from_str::("NotMyStruct(x: 4, y: 2)"), + check_from_str_bytes_reader::( + "NotMyStruct(x: 4, y: 2)", err( - ExpectedDifferentStructName { + Error::ExpectedDifferentStructName { expected: "MyStruct", - found: String::from("NotMyStruct") + found: String::from("NotMyStruct"), }, 1, - 12 - ) + 12, + ), ); - assert_eq!(from_str::<(u8, bool)>("'c'"), err(ExpectedStructLike, 1, 1)); - assert_eq!(from_str::("notabool"), err(ExpectedBoolean, 1, 1)); + check_from_str_bytes_reader::<(u8, bool)>("'c'", err(Error::ExpectedStructLike, 1, 1)); + check_from_str_bytes_reader::("notabool", err(Error::ExpectedBoolean, 1, 1)); - assert_eq!( - from_str::("MyStruct(\n x: true)"), - err(ExpectedFloat, 2, 8) + check_from_str_bytes_reader::( + "MyStruct(\n x: true)", + err(Error::ExpectedFloat, 2, 8), ); - assert_eq!( - from_str::("MyStruct(\n x: 3.5, \n y:)"), - err(ExpectedFloat, 3, 7) + check_from_str_bytes_reader::( + "MyStruct(\n x: 3.5, \n y:)", + err(Error::ExpectedFloat, 3, 7), ); } #[test] fn test_perm_ws() { - assert_eq!( - from_str::("\nMyStruct \t ( \n x : 3.5 , \t y\n: 4.5 \n ) \t\n"), - Ok(MyStruct { x: 3.5, y: 4.5 }) + check_from_str_bytes_reader( + "\nMyStruct \t ( \n x : 3.5 , \t y\n: 4.5 \n ) \t\n", + Ok(MyStruct { x: 3.5, y: 4.5 }), ); } @@ -215,10 +399,24 @@ fn untagged() { enum Untagged { U8(u8), Bool(bool), + Value(crate::Value), } - assert_eq!(from_str::("true").unwrap(), Untagged::Bool(true)); - assert_eq!(from_str::("8").unwrap(), Untagged::U8(8)); + check_from_str_bytes_reader("true", Ok(Untagged::Bool(true))); + check_from_str_bytes_reader("8", Ok(Untagged::U8(8))); + + // Check for a failure in Deserializer::check_struct_type + // - untagged enum and a leading identifier trigger the serde content enum path + // - serde content uses deserialize_any, which retriggers the struct type check + // - struct type check inside a serde content performs a full newtype check + // - newtype check fails on the unclosed struct + check_from_str_bytes_reader::( + "Value(()", + Err(crate::error::SpannedError { + code: crate::Error::Eof, + position: crate::error::Position { line: 1, col: 9 }, + }), + ); } #[test] @@ -230,47 +428,40 @@ fn rename() { #[serde(rename = "triangle-list")] TriangleList, } - assert_eq!(from_str::("r#2d").unwrap(), Foo::D2); - assert_eq!( - from_str::("r#triangle-list").unwrap(), - Foo::TriangleList - ); + + check_from_str_bytes_reader("r#2d", Ok(Foo::D2)); + check_from_str_bytes_reader("r#triangle-list", Ok(Foo::TriangleList)); } #[test] fn forgot_apostrophes() { - let de: SpannedResult<(i32, String)> = from_str("(4, \"Hello)"); - - assert!(matches!( - de, + check_from_str_bytes_reader::<(i32, String)>( + "(4, \"Hello)", Err(SpannedError { code: Error::ExpectedStringEnd, - position: _, - }) - )); + position: Position { line: 1, col: 6 }, + }), + ); } #[test] fn expected_attribute() { - let de: SpannedResult = from_str("#\"Hello\""); - - assert_eq!(de, err(Error::ExpectedAttribute, 1, 2)); + check_from_str_bytes_reader::("#\"Hello\"", err(Error::ExpectedAttribute, 1, 2)); } #[test] fn expected_attribute_end() { - let de: SpannedResult = from_str("#![enable(unwrap_newtypes) \"Hello\""); - - assert_eq!(de, err(Error::ExpectedAttributeEnd, 1, 28)); + check_from_str_bytes_reader::( + "#![enable(unwrap_newtypes) \"Hello\"", + err(Error::ExpectedAttributeEnd, 1, 28), + ); } #[test] fn invalid_attribute() { - let de: SpannedResult = from_str("#![enable(invalid)] \"Hello\""); - - assert_eq!( - de, - err(Error::NoSuchExtension("invalid".to_string()), 1, 18) + check_from_str_bytes_reader::( + "#![enable(invalid)] \"Hello\"", + err(Error::NoSuchExtension("invalid".to_string()), 1, 18), ); } @@ -278,22 +469,22 @@ fn invalid_attribute() { fn multiple_attributes() { #[derive(Debug, Deserialize, PartialEq)] struct New(String); - let de: SpannedResult = - from_str("#![enable(unwrap_newtypes)] #![enable(unwrap_newtypes)] \"Hello\""); - assert_eq!(de, Ok(New("Hello".to_owned()))); + check_from_str_bytes_reader( + "#![enable(unwrap_newtypes)] #![enable(unwrap_newtypes)] \"Hello\"", + Ok(New("Hello".to_owned())), + ); } #[test] fn uglified_attribute() { - let de: SpannedResult<()> = from_str( + check_from_str_bytes_reader( "# !\ // We definitely want to add a comment here [\t\tenable( // best style ever unwrap_newtypes ) ] ()", + Ok(()), ); - - assert_eq!(de, Ok(())); } #[test] @@ -303,7 +494,7 @@ fn implicit_some() { fn de(s: &str) -> Option { let enable = "#![enable(implicit_some)]\n".to_string(); - from_str::>(&(enable + s)).unwrap() + super::from_str::>(&(enable + s)).unwrap() } assert_eq!(de("'c'"), Some('c')); @@ -323,41 +514,165 @@ fn implicit_some() { #[test] fn ws_tuple_newtype_variant() { - assert_eq!(Ok(MyEnum::B(true)), from_str("B ( \n true \n ) ")); + check_from_str_bytes_reader("B ( \n true \n ) ", Ok(MyEnum::B(true))); } #[test] fn test_byte_stream() { - assert_eq!( + check_from_str_bytes_reader( + "BytesStruct( small:[1, 2], large:b\"\\x01\\x02\\x03\\x04\" )", Ok(BytesStruct { small: vec![1, 2], - large: vec![1, 2, 3, 4] + large: vec![1, 2, 3, 4], }), - from_str("BytesStruct( small:[1, 2], large:\"AQIDBA==\" )"), ); } #[test] fn test_numbers() { - assert_eq!( - Ok(vec![1234, 12345, 123456, 1234567, 555_555]), - from_str("[1_234, 12_345, 1_2_3_4_5_6, 1_234_567, 5_55_55_5]"), + check_from_str_bytes_reader( + "[1_234, 12_345, 1_2_3_4_5_6, 1_234_567, 5_55_55_5]", + Ok(vec![1234, 12345, 123_456, 1_234_567, 555_555]), ); } -fn de_any_number(s: &str) -> AnyNum { - let mut bytes = Bytes::new(s.as_bytes()).unwrap(); +fn check_de_any_number< + T: Copy + PartialEq + std::fmt::Debug + Into + serde::de::DeserializeOwned, +>( + s: &str, + cmp: T, +) { + let mut parser = Parser::new(s).unwrap(); + let number = parser.any_number().unwrap(); - bytes.any_num().unwrap() + assert_eq!(number, Number::new(cmp)); + assert_eq!( + Number::new(super::from_str::(s).unwrap()), + Number::new(cmp) + ); } #[test] fn test_any_number_precision() { - assert_eq!(de_any_number("1"), AnyNum::U8(1)); - assert_eq!(de_any_number("+1"), AnyNum::I8(1)); - assert_eq!(de_any_number("-1"), AnyNum::I8(-1)); - assert_eq!(de_any_number("-1.0"), AnyNum::F32(-1.0)); - assert_eq!(de_any_number("1."), AnyNum::F32(1.)); - assert_eq!(de_any_number("-1."), AnyNum::F32(-1.)); - assert_eq!(de_any_number("0.3"), AnyNum::F64(0.3)); + check_de_any_number("1", 1_u8); + check_de_any_number("+1", 1_u8); + check_de_any_number("-1", -1_i8); + check_de_any_number("-1.0", -1.0_f32); + check_de_any_number("1.", 1.0_f32); + check_de_any_number("-1.", -1.0_f32); + check_de_any_number(".3", 0.3_f64); + check_de_any_number("-.3", -0.3_f64); + check_de_any_number("+.3", 0.3_f64); + check_de_any_number("0.3", 0.3_f64); + check_de_any_number("NaN", f32::NAN); + check_de_any_number("-NaN", -f32::NAN); + check_de_any_number("inf", f32::INFINITY); + check_de_any_number("-inf", f32::NEG_INFINITY); + + macro_rules! test_min { + ($($ty:ty),*) => { + $(check_de_any_number(&format!("{}", <$ty>::MIN), <$ty>::MIN);)* + }; + } + + macro_rules! test_max { + ($($ty:ty),*) => { + $(check_de_any_number(&format!("{}", <$ty>::MAX), <$ty>::MAX);)* + }; + } + + test_min! { i8, i16, i32, i64, f64 } + test_max! { u8, u16, u32, u64, f64 } + #[cfg(feature = "integer128")] + test_min! { i128 } + #[cfg(feature = "integer128")] + test_max! { u128 } +} + +#[test] +fn test_value_special_floats() { + use crate::{from_str, value::Number, Value}; + + assert_eq!( + from_str("NaN"), + Ok(Value::Number(Number::F32(f32::NAN.into()))) + ); + assert_eq!( + from_str("+NaN"), + Ok(Value::Number(Number::F32(f32::NAN.into()))) + ); + assert_eq!( + from_str("-NaN"), + Ok(Value::Number(Number::F32((-f32::NAN).into()))) + ); + + assert_eq!( + from_str("inf"), + Ok(Value::Number(Number::F32(f32::INFINITY.into()))) + ); + assert_eq!( + from_str("+inf"), + Ok(Value::Number(Number::F32(f32::INFINITY.into()))) + ); + assert_eq!( + from_str("-inf"), + Ok(Value::Number(Number::F32(f32::NEG_INFINITY.into()))) + ); +} + +#[test] +fn test_leading_whitespace() { + check_from_str_bytes_reader(" +1", Ok(1_u8)); + check_from_str_bytes_reader(" EmptyStruct1", Ok(EmptyStruct1)); +} + +fn check_from_str_bytes_reader( + ron: &str, + check: SpannedResult, +) { + let res_str = super::from_str::(ron); + assert_eq!(res_str, check); + + let res_bytes = super::from_bytes::(ron.as_bytes()); + assert_eq!(res_bytes, check); + + let res_reader = super::from_reader::<&[u8], T>(ron.as_bytes()); + assert_eq!(res_reader, check); +} + +#[test] +fn test_remainder() { + let mut deserializer = super::Deserializer::from_str(" 42 ").unwrap(); + assert_eq!( + ::deserialize(&mut deserializer).unwrap(), + 42 + ); + assert_eq!(deserializer.remainder(), " "); + assert_eq!(deserializer.end(), Ok(())); + + let mut deserializer = super::Deserializer::from_str(" 42 37 ").unwrap(); + assert_eq!( + ::deserialize(&mut deserializer).unwrap(), + 42 + ); + assert_eq!(deserializer.remainder(), " 37 "); + assert_eq!(deserializer.end(), Err(Error::TrailingCharacters)); +} + +#[test] +fn boolean_struct_name() { + check_from_str_bytes_reader::( + "true_", + Err(SpannedError { + code: Error::ExpectedBoolean, + position: Position { line: 1, col: 1 }, + }), + ); + check_from_str_bytes_reader::( + "false_", + Err(SpannedError { + code: Error::ExpectedBoolean, + position: Position { line: 1, col: 1 }, + }), + ); } diff --git a/third_party/rust/ron/src/de/value.rs b/third_party/rust/ron/src/de/value.rs index 5883a37adccd..b7e69b619b92 100644 --- a/third_party/rust/ron/src/de/value.rs +++ b/third_party/rust/ron/src/de/value.rs @@ -49,6 +49,27 @@ impl<'de> Visitor<'de> for ValueVisitor { Ok(Value::Bool(v)) } + fn visit_i8(self, v: i8) -> Result + where + E: Error, + { + Ok(Value::Number(Number::new(v))) + } + + fn visit_i16(self, v: i16) -> Result + where + E: Error, + { + Ok(Value::Number(Number::new(v))) + } + + fn visit_i32(self, v: i32) -> Result + where + E: Error, + { + Ok(Value::Number(Number::new(v))) + } + fn visit_i64(self, v: i64) -> Result where E: Error, @@ -61,7 +82,28 @@ impl<'de> Visitor<'de> for ValueVisitor { where E: Error, { - self.visit_f64(v as f64) + Ok(Value::Number(Number::new(v))) + } + + fn visit_u8(self, v: u8) -> Result + where + E: Error, + { + Ok(Value::Number(Number::new(v))) + } + + fn visit_u16(self, v: u16) -> Result + where + E: Error, + { + Ok(Value::Number(Number::new(v))) + } + + fn visit_u32(self, v: u32) -> Result + where + E: Error, + { + Ok(Value::Number(Number::new(v))) } fn visit_u64(self, v: u64) -> Result @@ -76,7 +118,14 @@ impl<'de> Visitor<'de> for ValueVisitor { where E: Error, { - self.visit_f64(v as f64) + Ok(Value::Number(Number::new(v))) + } + + fn visit_f32(self, v: f32) -> Result + where + E: Error, + { + Ok(Value::Number(Number::new(v))) } fn visit_f64(self, v: f64) -> Result @@ -118,7 +167,7 @@ impl<'de> Visitor<'de> for ValueVisitor { where E: Error, { - self.visit_string(String::from_utf8(v).map_err(|e| Error::custom(format!("{}", e)))?) + Ok(Value::Bytes(v)) } fn visit_none(self) -> Result @@ -173,7 +222,12 @@ impl<'de> Visitor<'de> for ValueVisitor { { let mut res: Map = Map::new(); - while let Some(entry) = map.next_entry()? { + #[cfg(feature = "indexmap")] + if let Some(cap) = map.size_hint() { + res.0.reserve_exact(cap); + } + + while let Some(entry) = map.next_entry::()? { res.insert(entry.0, entry.1); } @@ -210,9 +264,9 @@ mod tests { assert_eq!( eval("(3, 4.0, 5.0)"), Value::Seq(vec![ - Value::Number(Number::new(3)), - Value::Number(Number::new(4.0)), - Value::Number(Number::new(5.0)), + Value::Number(Number::U8(3)), + Value::Number(Number::F32(4.0.into())), + Value::Number(Number::F32(5.0.into())), ],), ); } @@ -223,9 +277,9 @@ mod tests { eval("(true, 3, 4, 5.0)"), Value::Seq(vec![ Value::Bool(true), - Value::Number(Number::new(3)), - Value::Number(Number::new(4)), - Value::Number(Number::new(5.0)), + Value::Number(Number::U8(3)), + Value::Number(Number::U8(4)), + Value::Number(Number::F32(5.0.into())), ]), ); } @@ -248,9 +302,9 @@ mod tests { assert_eq!( eval("(inf, -inf, NaN)"), Value::Seq(vec![ - Value::Number(Number::new(std::f64::INFINITY)), - Value::Number(Number::new(std::f64::NEG_INFINITY)), - Value::Number(Number::new(std::f64::NAN)), + Value::Number(Number::new(std::f32::INFINITY)), + Value::Number(Number::new(std::f32::NEG_INFINITY)), + Value::Number(Number::new(std::f32::NAN)), ]), ); } @@ -279,11 +333,11 @@ mod tests { vec![ ( Value::String("width".to_owned()), - Value::Number(Number::new(20)), + Value::Number(Number::U8(20)), ), ( Value::String("height".to_owned()), - Value::Number(Number::new(5)), + Value::Number(Number::U8(5)), ), ( Value::String("name".to_owned()), @@ -297,11 +351,11 @@ mod tests { vec![ ( Value::String("width".to_owned()), - Value::Number(Number::new(10.0)), + Value::Number(Number::F32(10.0.into())), ), ( Value::String("height".to_owned()), - Value::Number(Number::new(10.0)), + Value::Number(Number::F32(10.0.into())), ), ( Value::String("name".to_owned()), @@ -313,15 +367,15 @@ mod tests { vec![ ( Value::String("Enemy1".to_owned()), - Value::Number(Number::new(3)), + Value::Number(Number::U8(3)), ), ( Value::String("Enemy2".to_owned()), - Value::Number(Number::new(5)), + Value::Number(Number::U8(5)), ), ( Value::String("Enemy3".to_owned()), - Value::Number(Number::new(7)), + Value::Number(Number::U8(7)), ), ] .into_iter() @@ -335,4 +389,48 @@ mod tests { ])))) ); } + + #[test] + fn test_struct() { + assert_eq!( + eval("(a:42)"), + Value::Map( + [( + Value::String(String::from("a")), + Value::Number(Number::U8(42)) + )] + .into_iter() + .collect() + ), + ); + assert_eq!( + eval("(r#a:42)"), + Value::Map( + [( + Value::String(String::from("a")), + Value::Number(Number::U8(42)) + )] + .into_iter() + .collect() + ), + ); + assert_eq!( + "(r#:42)".parse::().unwrap_err(), + crate::error::SpannedError { + code: crate::Error::ExpectedString, + position: crate::error::Position { line: 1, col: 4 }, + }, + ); + + // Check for a failure in Deserializer::check_struct_type + // - opening brace triggers the struct type check + // - unclosed block comment fails the whitespace skip + assert_eq!( + "( /*".parse::().unwrap_err(), + crate::error::SpannedError { + code: crate::Error::UnclosedBlockComment, + position: crate::error::Position { line: 1, col: 5 }, + }, + ); + } } diff --git a/third_party/rust/ron/src/error.rs b/third_party/rust/ron/src/error.rs index 406fb058c37e..46be80dd61ed 100644 --- a/third_party/rust/ron/src/error.rs +++ b/third_party/rust/ron/src/error.rs @@ -1,11 +1,17 @@ -use std::{error::Error as StdError, fmt, io, str::Utf8Error, string::FromUtf8Error}; +use std::{ + error::Error as StdError, + fmt, io, + str::{self, Utf8Error}, +}; use serde::{de, ser}; +use unicode_ident::is_xid_continue; -use crate::parse::{is_ident_first_char, is_ident_other_char, is_ident_raw_char, BASE64_ENGINE}; +use crate::parse::{is_ident_first_char, is_ident_raw_char}; /// This type represents all possible errors that can occur when /// serializing or deserializing RON data. +#[allow(clippy::module_name_repetitions)] #[derive(Clone, Debug, PartialEq, Eq)] pub struct SpannedError { pub code: Error, @@ -18,8 +24,13 @@ pub type SpannedResult = std::result::Result; #[derive(Clone, Debug, PartialEq, Eq)] #[non_exhaustive] pub enum Error { + Fmt, Io(String), Message(String), + #[deprecated( + since = "0.9.0", + note = "ambiguous base64 byte strings are replaced by strongly typed Rusty b\"byte strings\"" + )] Base64Error(base64::DecodeError), Eof, ExpectedArray, @@ -29,6 +40,7 @@ pub enum Error { ExpectedBoolean, ExpectedComma, ExpectedChar, + ExpectedByteLiteral, ExpectedFloat, FloatUnderscore, ExpectedInteger, @@ -46,18 +58,24 @@ pub enum Error { ExpectedStructLikeEnd, ExpectedUnit, ExpectedString, + ExpectedByteString, ExpectedStringEnd, ExpectedIdentifier, InvalidEscape(&'static str), IntegerOutOfBounds, + InvalidIntegerDigit { + digit: char, + base: u8, + }, NoSuchExtension(String), UnclosedBlockComment, + UnclosedLineComment, UnderscoreAtBeginning, - UnexpectedByte(char), + UnexpectedChar(char), Utf8Error(Utf8Error), TrailingCharacters, @@ -90,25 +108,25 @@ pub enum Error { }, InvalidIdentifier(String), SuggestRawIdentifier(String), + ExpectedRawValue, ExceededRecursionLimit, + ExpectedStructName(String), } impl fmt::Display for SpannedError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if (self.position == Position { line: 0, col: 0 }) { - write!(f, "{}", self.code) - } else { - write!(f, "{}: {}", self.position, self.code) - } + write!(f, "{}: {}", self.position, self.code) } } impl fmt::Display for Error { + #[allow(clippy::too_many_lines)] fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match *self { - Error::Io(ref s) => f.write_str(s), - Error::Message(ref s) => f.write_str(s), - Error::Base64Error(ref e) => fmt::Display::fmt(e, f), + Error::Fmt => f.write_str("Formatting RON failed"), + Error::Io(ref s) | Error::Message(ref s) => f.write_str(s), + #[allow(deprecated)] + Error::Base64Error(ref e) => write!(f, "Invalid base64: {}", e), Error::Eof => f.write_str("Unexpected end of RON"), Error::ExpectedArray => f.write_str("Expected opening `[`"), Error::ExpectedArrayEnd => f.write_str("Expected closing `]`"), @@ -119,11 +137,14 @@ impl fmt::Display for Error { Error::ExpectedBoolean => f.write_str("Expected boolean"), Error::ExpectedComma => f.write_str("Expected comma"), Error::ExpectedChar => f.write_str("Expected char"), + Error::ExpectedByteLiteral => f.write_str("Expected byte literal"), Error::ExpectedFloat => f.write_str("Expected float"), Error::FloatUnderscore => f.write_str("Unexpected underscore in float"), Error::ExpectedInteger => f.write_str("Expected integer"), Error::ExpectedOption => f.write_str("Expected option"), - Error::ExpectedOptionEnd => f.write_str("Expected closing `)`"), + Error::ExpectedOptionEnd | Error::ExpectedStructLikeEnd => { + f.write_str("Expected closing `)`") + } Error::ExpectedMap => f.write_str("Expected opening `{`"), Error::ExpectedMapColon => f.write_str("Expected colon"), Error::ExpectedMapEnd => f.write_str("Expected closing `}`"), @@ -144,22 +165,29 @@ impl fmt::Display for Error { write!(f, "Expected opening `(` for struct {}", Identifier(name)) } } - Error::ExpectedStructLikeEnd => f.write_str("Expected closing `)`"), Error::ExpectedUnit => f.write_str("Expected unit"), Error::ExpectedString => f.write_str("Expected string"), + Error::ExpectedByteString => f.write_str("Expected byte string"), Error::ExpectedStringEnd => f.write_str("Expected end of string"), Error::ExpectedIdentifier => f.write_str("Expected identifier"), Error::InvalidEscape(s) => f.write_str(s), Error::IntegerOutOfBounds => f.write_str("Integer is out of bounds"), + Error::InvalidIntegerDigit { digit, base } => { + write!(f, "Invalid digit {:?} for base {} integers", digit, base) + } Error::NoSuchExtension(ref name) => { write!(f, "No RON extension named {}", Identifier(name)) } Error::Utf8Error(ref e) => fmt::Display::fmt(e, f), Error::UnclosedBlockComment => f.write_str("Unclosed block comment"), + Error::UnclosedLineComment => f.write_str( + "`ron::value::RawValue` cannot end in unclosed line comment, \ + try using a block comment or adding a newline", + ), Error::UnderscoreAtBeginning => { - f.write_str("Unexpected leading underscore in an integer") + f.write_str("Unexpected leading underscore in a number") } - Error::UnexpectedByte(ref byte) => write!(f, "Unexpected byte {:?}", byte), + Error::UnexpectedChar(c) => write!(f, "Unexpected char {:?}", c), Error::TrailingCharacters => f.write_str("Non-whitespace trailing characters"), Error::InvalidValueForType { ref expected, @@ -195,7 +223,7 @@ impl fmt::Display for Error { write!(f, "variant named {}", Identifier(found))?; if let Some(outer) = outer { - write!(f, "in enum {}", Identifier(outer))?; + write!(f, " in enum {}", Identifier(outer))?; } write!( @@ -215,7 +243,7 @@ impl fmt::Display for Error { write!(f, "Unexpected field named {}", Identifier(found))?; if let Some(outer) = outer { - write!(f, "in {}", Identifier(outer))?; + write!(f, " in {}", Identifier(outer))?; } write!( @@ -228,7 +256,7 @@ impl fmt::Display for Error { ) } Error::MissingStructField { field, ref outer } => { - write!(f, "Unexpected missing field {}", Identifier(field))?; + write!(f, "Unexpected missing field named {}", Identifier(field))?; match outer { Some(outer) => write!(f, " in {}", Identifier(outer)), @@ -236,7 +264,7 @@ impl fmt::Display for Error { } } Error::DuplicateStructField { field, ref outer } => { - write!(f, "Unexpected duplicate field {}", Identifier(field))?; + write!(f, "Unexpected duplicate field named {}", Identifier(field))?; match outer { Some(outer) => write!(f, " in {}", Identifier(outer)), @@ -246,20 +274,38 @@ impl fmt::Display for Error { Error::InvalidIdentifier(ref invalid) => write!(f, "Invalid identifier {:?}", invalid), Error::SuggestRawIdentifier(ref identifier) => write!( f, - "Found invalid std identifier `{}`, try the raw identifier `r#{}` instead", + "Found invalid std identifier {:?}, try the raw identifier `r#{}` instead", identifier, identifier ), - Error::ExceededRecursionLimit => f.write_str("Exceeded recursion limit, try increasing the limit and using `serde_stacker` to protect against a stack overflow"), + Error::ExpectedRawValue => f.write_str("Expected a `ron::value::RawValue`"), + Error::ExceededRecursionLimit => f.write_str( + "Exceeded recursion limit, try increasing `ron::Options::recursion_limit` \ + and using `serde_stacker` to protect against a stack overflow", + ), + Error::ExpectedStructName(ref name) => write!( + f, + "Expected the explicit struct name {}, but none was found", + Identifier(name) + ), } } } -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] pub struct Position { pub line: usize, pub col: usize, } +impl Position { + pub(crate) fn from_src_end(src: &str) -> Position { + let line = 1 + src.chars().filter(|&c| c == '\n').count(); + let col = 1 + src.chars().rev().take_while(|&c| c != '\n').count(); + + Self { line, col } + } +} + impl fmt::Display for Position { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}:{}", self.line, self.col) @@ -291,29 +337,30 @@ impl de::Error for Error { impl<'a> fmt::Display for UnexpectedSerdeTypeValue<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - use de::Unexpected::*; - match self.0 { - Bool(b) => write!(f, "the boolean `{}`", b), - Unsigned(i) => write!(f, "the unsigned integer `{}`", i), - Signed(i) => write!(f, "the signed integer `{}`", i), - Float(n) => write!(f, "the floating point number `{}`", n), - Char(c) => write!(f, "the UTF-8 character `{}`", c), - Str(s) => write!(f, "the string {:?}", s), - Bytes(b) => write!(f, "the bytes \"{}\"", { - base64::display::Base64Display::new(b, &BASE64_ENGINE) + de::Unexpected::Bool(b) => write!(f, "the boolean `{}`", b), + de::Unexpected::Unsigned(i) => write!(f, "the unsigned integer `{}`", i), + de::Unexpected::Signed(i) => write!(f, "the signed integer `{}`", i), + de::Unexpected::Float(n) => write!(f, "the floating point number `{}`", n), + de::Unexpected::Char(c) => write!(f, "the UTF-8 character `{}`", c), + de::Unexpected::Str(s) => write!(f, "the string {:?}", s), + de::Unexpected::Bytes(b) => write!(f, "the byte string b\"{}\"", { + b.iter() + .flat_map(|c| std::ascii::escape_default(*c)) + .map(char::from) + .collect::() }), - Unit => write!(f, "a unit value"), - Option => write!(f, "an optional value"), - NewtypeStruct => write!(f, "a newtype struct"), - Seq => write!(f, "a sequence"), - Map => write!(f, "a map"), - Enum => write!(f, "an enum"), - UnitVariant => write!(f, "a unit variant"), - NewtypeVariant => write!(f, "a newtype variant"), - TupleVariant => write!(f, "a tuple variant"), - StructVariant => write!(f, "a struct variant"), - Other(other) => f.write_str(other), + de::Unexpected::Unit => write!(f, "a unit value"), + de::Unexpected::Option => write!(f, "an optional value"), + de::Unexpected::NewtypeStruct => write!(f, "a newtype struct"), + de::Unexpected::Seq => write!(f, "a sequence"), + de::Unexpected::Map => write!(f, "a map"), + de::Unexpected::Enum => write!(f, "an enum"), + de::Unexpected::UnitVariant => write!(f, "a unit variant"), + de::Unexpected::NewtypeVariant => write!(f, "a newtype variant"), + de::Unexpected::TupleVariant => write!(f, "a tuple variant"), + de::Unexpected::StructVariant => write!(f, "a struct variant"), + de::Unexpected::Other(other) => f.write_str(other), } } } @@ -370,9 +417,9 @@ impl From for Error { } } -impl From for Error { - fn from(e: FromUtf8Error) -> Self { - Error::Utf8Error(e.utf8_error()) +impl From for Error { + fn from(_: fmt::Error) -> Self { + Error::Fmt } } @@ -382,15 +429,6 @@ impl From for Error { } } -impl From for SpannedError { - fn from(e: io::Error) -> Self { - SpannedError { - code: e.into(), - position: Position { line: 0, col: 0 }, - } - } -} - impl From for Error { fn from(e: SpannedError) -> Self { e.code @@ -413,14 +451,14 @@ impl fmt::Display for OneOf { Identifier(a1), Identifier(a2) ), - [a1, ref alts @ ..] => { + [a1, ref alts @ .., an] => { write!(f, "expected one of {}", Identifier(a1))?; for alt in alts { write!(f, ", {}", Identifier(alt))?; } - f.write_str(" instead") + write!(f, ", or {} instead", Identifier(an)) } } } @@ -430,16 +468,236 @@ struct Identifier<'a>(&'a str); impl<'a> fmt::Display for Identifier<'a> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if self.0.is_empty() || !self.0.as_bytes().iter().copied().all(is_ident_raw_char) { + if self.0.is_empty() || !self.0.chars().all(is_ident_raw_char) { return write!(f, "{:?}_[invalid identifier]", self.0); } - let mut bytes = self.0.as_bytes().iter().copied(); + let mut chars = self.0.chars(); - if !bytes.next().map_or(false, is_ident_first_char) || !bytes.all(is_ident_other_char) { + if !chars.next().map_or(false, is_ident_first_char) || !chars.all(is_xid_continue) { write!(f, "`r#{}`", self.0) } else { write!(f, "`{}`", self.0) } } } + +#[cfg(test)] +mod tests { + use serde::{de::Error as DeError, de::Unexpected, ser::Error as SerError}; + + use super::{Error, Position, SpannedError}; + + #[test] + fn error_messages() { + check_error_message(&Error::from(std::fmt::Error), "Formatting RON failed"); + check_error_message( + &Error::from(std::io::Error::new( + std::io::ErrorKind::InvalidData, + "my-error", + )), + "my-error", + ); + check_error_message(&::custom("my-ser-error"), "my-ser-error"); + check_error_message(&::custom("my-de-error"), "my-de-error"); + #[allow(deprecated)] + check_error_message( + &Error::Base64Error(base64::DecodeError::InvalidPadding), + "Invalid base64: Invalid padding", + ); + check_error_message(&Error::Eof, "Unexpected end of RON"); + check_error_message(&Error::ExpectedArray, "Expected opening `[`"); + check_error_message(&Error::ExpectedArrayEnd, "Expected closing `]`"); + check_error_message( + &Error::ExpectedAttribute, + "Expected an `#![enable(...)]` attribute", + ); + check_error_message( + &Error::ExpectedAttributeEnd, + "Expected closing `)]` after the enable attribute", + ); + check_error_message(&Error::ExpectedBoolean, "Expected boolean"); + check_error_message(&Error::ExpectedComma, "Expected comma"); + check_error_message(&Error::ExpectedChar, "Expected char"); + check_error_message(&Error::ExpectedByteLiteral, "Expected byte literal"); + check_error_message(&Error::ExpectedFloat, "Expected float"); + check_error_message(&Error::FloatUnderscore, "Unexpected underscore in float"); + check_error_message(&Error::ExpectedInteger, "Expected integer"); + check_error_message(&Error::ExpectedOption, "Expected option"); + check_error_message(&Error::ExpectedOptionEnd, "Expected closing `)`"); + check_error_message(&Error::ExpectedStructLikeEnd, "Expected closing `)`"); + check_error_message(&Error::ExpectedMap, "Expected opening `{`"); + check_error_message(&Error::ExpectedMapColon, "Expected colon"); + check_error_message(&Error::ExpectedMapEnd, "Expected closing `}`"); + check_error_message( + &Error::ExpectedDifferentStructName { + expected: "raw+identifier", + found: String::from("identifier"), + }, + "Expected struct `r#raw+identifier` but found `identifier`", + ); + check_error_message(&Error::ExpectedStructLike, "Expected opening `(`"); + check_error_message( + &Error::ExpectedNamedStructLike(""), + "Expected only opening `(`, no name, for un-nameable struct", + ); + check_error_message( + &Error::ExpectedNamedStructLike("_ident"), + "Expected opening `(` for struct `_ident`", + ); + check_error_message(&Error::ExpectedUnit, "Expected unit"); + check_error_message(&Error::ExpectedString, "Expected string"); + check_error_message(&Error::ExpectedByteString, "Expected byte string"); + check_error_message(&Error::ExpectedStringEnd, "Expected end of string"); + check_error_message(&Error::ExpectedIdentifier, "Expected identifier"); + check_error_message(&Error::InvalidEscape("Invalid escape"), "Invalid escape"); + check_error_message(&Error::IntegerOutOfBounds, "Integer is out of bounds"); + check_error_message( + &Error::InvalidIntegerDigit { + digit: 'q', + base: 16, + }, + "Invalid digit 'q' for base 16 integers", + ); + check_error_message( + &Error::NoSuchExtension(String::from("unknown")), + "No RON extension named `unknown`", + ); + check_error_message(&Error::UnclosedBlockComment, "Unclosed block comment"); + check_error_message( + &Error::UnclosedLineComment, + "`ron::value::RawValue` cannot end in unclosed line comment, \ + try using a block comment or adding a newline", + ); + check_error_message( + &Error::UnderscoreAtBeginning, + "Unexpected leading underscore in a number", + ); + check_error_message(&Error::UnexpectedChar('🦀'), "Unexpected char \'🦀\'"); + #[allow(invalid_from_utf8)] + check_error_message( + &Error::Utf8Error(std::str::from_utf8(b"error: \xff\xff\xff\xff").unwrap_err()), + "invalid utf-8 sequence of 1 bytes from index 7", + ); + check_error_message( + &Error::TrailingCharacters, + "Non-whitespace trailing characters", + ); + check_error_message( + &Error::invalid_value(Unexpected::Enum, &"struct `Hi`"), + "Expected struct `Hi` but found an enum instead", + ); + check_error_message( + &Error::invalid_length(0, &"two bees"), + "Expected two bees but found zero elements instead", + ); + check_error_message( + &Error::invalid_length(1, &"two bees"), + "Expected two bees but found one element instead", + ); + check_error_message( + &Error::invalid_length(3, &"two bees"), + "Expected two bees but found 3 elements instead", + ); + check_error_message( + &Error::unknown_variant("unknown", &[]), + "Unexpected enum variant named `unknown`, there are no variants", + ); + check_error_message( + &Error::NoSuchEnumVariant { + expected: &["A", "B+C"], + found: String::from("D"), + outer: Some(String::from("E")), + }, + "Unexpected variant named `D` in enum `E`, \ + expected either `A` or `r#B+C` instead", + ); + check_error_message( + &Error::unknown_field("unknown", &[]), + "Unexpected field named `unknown`, there are no fields", + ); + check_error_message( + &Error::NoSuchStructField { + expected: &["a"], + found: String::from("b"), + outer: Some(String::from("S")), + }, + "Unexpected field named `b` in `S`, expected `a` instead", + ); + check_error_message( + &Error::NoSuchStructField { + expected: &["a", "b+c", "d"], + found: String::from("e"), + outer: Some(String::from("S")), + }, + "Unexpected field named `e` in `S`, \ + expected one of `a`, `r#b+c`, or `d` instead", + ); + check_error_message( + &Error::missing_field("a"), + "Unexpected missing field named `a`", + ); + check_error_message( + &Error::MissingStructField { + field: "", + outer: Some(String::from("S+T")), + }, + "Unexpected missing field named \"\"_[invalid identifier] in `r#S+T`", + ); + check_error_message( + &Error::duplicate_field("a"), + "Unexpected duplicate field named `a`", + ); + check_error_message( + &Error::DuplicateStructField { + field: "b+c", + outer: Some(String::from("S+T")), + }, + "Unexpected duplicate field named `r#b+c` in `r#S+T`", + ); + check_error_message( + &Error::InvalidIdentifier(String::from("why+🦀+not")), + "Invalid identifier \"why+🦀+not\"", + ); + check_error_message( + &Error::SuggestRawIdentifier(String::from("raw+ident")), + "Found invalid std identifier \"raw+ident\", \ + try the raw identifier `r#raw+ident` instead", + ); + check_error_message( + &Error::ExpectedRawValue, + "Expected a `ron::value::RawValue`", + ); + check_error_message( + &Error::ExceededRecursionLimit, + "Exceeded recursion limit, try increasing `ron::Options::recursion_limit` \ + and using `serde_stacker` to protect against a stack overflow", + ); + check_error_message( + &Error::ExpectedStructName(String::from("Struct")), + "Expected the explicit struct name `Struct`, but none was found", + ); + } + + fn check_error_message(err: &T, msg: &str) { + assert_eq!(format!("{}", err), msg); + } + + #[test] + fn spanned_error_into_code() { + assert_eq!( + Error::from(SpannedError { + code: Error::Eof, + position: Position { line: 1, col: 1 } + }), + Error::Eof + ); + assert_eq!( + Error::from(SpannedError { + code: Error::ExpectedRawValue, + position: Position { line: 1, col: 1 } + }), + Error::ExpectedRawValue + ); + } +} diff --git a/third_party/rust/ron/src/extensions.rs b/third_party/rust/ron/src/extensions.rs index 0455a34dd371..c4862b79111b 100644 --- a/third_party/rust/ron/src/extensions.rs +++ b/third_party/rust/ron/src/extensions.rs @@ -1,31 +1,41 @@ use serde_derive::{Deserialize, Serialize}; +// GRCOV_EXCL_START bitflags::bitflags! { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)] pub struct Extensions: usize { const UNWRAP_NEWTYPES = 0x1; const IMPLICIT_SOME = 0x2; const UNWRAP_VARIANT_NEWTYPES = 0x4; + /// During serialization, this extension emits struct names. See also [`PrettyConfig::struct_names`](crate::ser::PrettyConfig::struct_names) for the [`PrettyConfig`](crate::ser::PrettyConfig) equivalent. + /// + /// During deserialization, this extension requires that structs' names are stated explicitly. + const EXPLICIT_STRUCT_NAMES = 0x8; } } +// GRCOV_EXCL_STOP impl Extensions { /// Creates an extension flag from an ident. - pub fn from_ident(ident: &[u8]) -> Option { - match ident { - b"unwrap_newtypes" => Some(Extensions::UNWRAP_NEWTYPES), - b"implicit_some" => Some(Extensions::IMPLICIT_SOME), - b"unwrap_variant_newtypes" => Some(Extensions::UNWRAP_VARIANT_NEWTYPES), - _ => None, + #[must_use] + pub fn from_ident(ident: &str) -> Option { + for (name, extension) in Extensions::all().iter_names() { + if ident == name.to_lowercase() { + return Some(extension); + } } + + None } } +// GRCOV_EXCL_START impl Default for Extensions { fn default() -> Self { Extensions::empty() } } +// GRCOV_EXCL_STOP #[cfg(test)] mod tests { @@ -39,17 +49,10 @@ mod tests { #[test] fn test_extension_serde() { - roundtrip_extensions(Extensions::default()); - roundtrip_extensions(Extensions::UNWRAP_NEWTYPES); - roundtrip_extensions(Extensions::IMPLICIT_SOME); - roundtrip_extensions(Extensions::UNWRAP_VARIANT_NEWTYPES); - roundtrip_extensions(Extensions::UNWRAP_NEWTYPES | Extensions::IMPLICIT_SOME); - roundtrip_extensions(Extensions::UNWRAP_NEWTYPES | Extensions::UNWRAP_VARIANT_NEWTYPES); - roundtrip_extensions(Extensions::IMPLICIT_SOME | Extensions::UNWRAP_VARIANT_NEWTYPES); - roundtrip_extensions( - Extensions::UNWRAP_NEWTYPES - | Extensions::IMPLICIT_SOME - | Extensions::UNWRAP_VARIANT_NEWTYPES, - ); + // iterate over the powerset of all extensions (i.e. every possible combination of extensions) + for bits in Extensions::empty().bits()..=Extensions::all().bits() { + let extensions = Extensions::from_bits_retain(bits); + roundtrip_extensions(extensions); + } } } diff --git a/third_party/rust/ron/src/lib.rs b/third_party/rust/ron/src/lib.rs index 638af2cb07f8..cb50da52afc0 100644 --- a/third_party/rust/ron/src/lib.rs +++ b/third_party/rust/ron/src/lib.rs @@ -1,5 +1,19 @@ +#![deny(clippy::correctness)] +#![deny(clippy::suspicious)] +#![deny(clippy::complexity)] +#![deny(clippy::perf)] +#![deny(clippy::style)] +#![warn(clippy::pedantic)] +#![cfg_attr(not(test), deny(clippy::unwrap_used))] +#![cfg_attr(not(test), deny(clippy::expect_used))] +#![deny(clippy::panic)] +#![warn(clippy::todo)] +#![deny(clippy::unimplemented)] +#![deny(clippy::unreachable)] +#![deny(unsafe_code)] +#![allow(clippy::missing_errors_doc)] // FIXME #![doc = include_str!("../README.md")] -#![doc(html_root_url = "https://docs.rs/ron/0.8.1")] +#![doc(html_root_url = "https://docs.rs/ron/0.9.0")] pub mod de; pub mod ser; diff --git a/third_party/rust/ron/src/options.rs b/third_party/rust/ron/src/options.rs index b0c5816bd42d..c7bf4429ea63 100644 --- a/third_party/rust/ron/src/options.rs +++ b/third_party/rust/ron/src/options.rs @@ -1,13 +1,13 @@ //! Roundtrip serde Options module. -use std::io; +use std::{fmt, io}; use serde::{de, ser}; use serde_derive::{Deserialize, Serialize}; use crate::{ de::Deserializer, - error::{Result, SpannedResult}, + error::{Position, Result, SpannedError, SpannedResult}, extensions::Extensions, ser::{PrettyConfig, Serializer}, }; @@ -27,7 +27,7 @@ use crate::{ /// /// assert_eq!(ser, "42"); /// ``` -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize)] // GRCOV_EXCL_LINE #[serde(default)] #[non_exhaustive] pub struct Options { @@ -92,15 +92,12 @@ impl Options { impl Options { /// A convenience function for building a deserializer /// and deserializing a value of type `T` from a reader. - pub fn from_reader(&self, mut rdr: R) -> SpannedResult + pub fn from_reader(&self, rdr: R) -> SpannedResult where R: io::Read, T: de::DeserializeOwned, { - let mut bytes = Vec::new(); - rdr.read_to_end(&mut bytes)?; - - self.from_bytes(&bytes) + self.from_reader_seed(rdr, std::marker::PhantomData) } /// A convenience function for building a deserializer @@ -109,7 +106,7 @@ impl Options { where T: de::Deserialize<'a>, { - self.from_bytes(s.as_bytes()) + self.from_str_seed(s, std::marker::PhantomData) } /// A convenience function for building a deserializer @@ -124,15 +121,34 @@ impl Options { /// A convenience function for building a deserializer /// and deserializing a value of type `T` from a reader /// and a seed. + // FIXME: panic is not actually possible, remove once utf8_chunks is stabilized + #[allow(clippy::missing_panics_doc)] pub fn from_reader_seed(&self, mut rdr: R, seed: S) -> SpannedResult where R: io::Read, S: for<'a> de::DeserializeSeed<'a, Value = T>, { let mut bytes = Vec::new(); - rdr.read_to_end(&mut bytes)?; - self.from_bytes_seed(&bytes, seed) + let io_err = if let Err(err) = rdr.read_to_end(&mut bytes) { + err + } else { + return self.from_bytes_seed(&bytes, seed); + }; + + // Try to compute a good error position for the I/O error + // FIXME: use [`utf8_chunks`](https://github.com/rust-lang/rust/issues/99543) once stabilised + #[allow(clippy::expect_used)] + let valid_input = match std::str::from_utf8(&bytes) { + Ok(valid_input) => valid_input, + Err(err) => std::str::from_utf8(&bytes[..err.valid_up_to()]) + .expect("source is valid up to error"), + }; + + Err(SpannedError { + code: io_err.into(), + position: Position::from_src_end(valid_input), + }) } /// A convenience function for building a deserializer @@ -142,7 +158,15 @@ impl Options { where S: de::DeserializeSeed<'a, Value = T>, { - self.from_bytes_seed(s.as_bytes(), seed) + let mut deserializer = Deserializer::from_str_with_options(s, self)?; + + let value = seed + .deserialize(&mut deserializer) + .map_err(|e| deserializer.span_error(e))?; + + deserializer.end().map_err(|e| deserializer.span_error(e))?; + + Ok(value) } /// A convenience function for building a deserializer @@ -152,7 +176,7 @@ impl Options { where S: de::DeserializeSeed<'a, Value = T>, { - let mut deserializer = Deserializer::from_bytes_with_options(s, self.clone())?; + let mut deserializer = Deserializer::from_bytes_with_options(s, self)?; let value = seed .deserialize(&mut deserializer) @@ -170,20 +194,20 @@ impl Options { /// [`to_writer_pretty`][Self::to_writer_pretty] instead. pub fn to_writer(&self, writer: W, value: &T) -> Result<()> where - W: io::Write, + W: fmt::Write, T: ?Sized + ser::Serialize, { - let mut s = Serializer::with_options(writer, None, self.clone())?; + let mut s = Serializer::with_options(writer, None, self)?; value.serialize(&mut s) } /// Serializes `value` into `writer` in a pretty way. pub fn to_writer_pretty(&self, writer: W, value: &T, config: PrettyConfig) -> Result<()> where - W: io::Write, + W: fmt::Write, T: ?Sized + ser::Serialize, { - let mut s = Serializer::with_options(writer, Some(config), self.clone())?; + let mut s = Serializer::with_options(writer, Some(config), self)?; value.serialize(&mut s) } @@ -196,10 +220,10 @@ impl Options { where T: ?Sized + ser::Serialize, { - let mut output = Vec::new(); - let mut s = Serializer::with_options(&mut output, None, self.clone())?; + let mut output = String::new(); + let mut s = Serializer::with_options(&mut output, None, self)?; value.serialize(&mut s)?; - Ok(String::from_utf8(output).expect("Ron should be utf-8")) + Ok(output) } /// Serializes `value` in the recommended RON layout in a pretty way. @@ -207,9 +231,9 @@ impl Options { where T: ?Sized + ser::Serialize, { - let mut output = Vec::new(); - let mut s = Serializer::with_options(&mut output, Some(config), self.clone())?; + let mut output = String::new(); + let mut s = Serializer::with_options(&mut output, Some(config), self)?; value.serialize(&mut s)?; - Ok(String::from_utf8(output).expect("Ron should be utf-8")) + Ok(output) } } diff --git a/third_party/rust/ron/src/parse.rs b/third_party/rust/ron/src/parse.rs index bed7f44916b2..b1d4f88316d3 100644 --- a/third_party/rust/ron/src/parse.rs +++ b/third_party/rust/ron/src/parse.rs @@ -2,117 +2,47 @@ use std::{ char::from_u32 as char_from_u32, - str::{from_utf8, from_utf8_unchecked, FromStr}, + str::{self, from_utf8, FromStr, Utf8Error}, }; -use base64::engine::general_purpose::{GeneralPurpose, STANDARD}; +use unicode_ident::{is_xid_continue, is_xid_start}; use crate::{ error::{Error, Position, Result, SpannedError, SpannedResult}, extensions::Extensions, + value::Number, }; -pub const BASE64_ENGINE: GeneralPurpose = STANDARD; - -// We have the following char categories. -const INT_CHAR: u8 = 1 << 0; // [0-9A-Fa-f_] -const FLOAT_CHAR: u8 = 1 << 1; // [0-9\.Ee+-_] -const IDENT_FIRST_CHAR: u8 = 1 << 2; // [A-Za-z_] -const IDENT_OTHER_CHAR: u8 = 1 << 3; // [A-Za-z_0-9] -const IDENT_RAW_CHAR: u8 = 1 << 4; // [A-Za-z_0-9\.+-] -const WHITESPACE_CHAR: u8 = 1 << 5; // [\n\t\r ] - -// We encode each char as belonging to some number of these categories. -const DIGIT: u8 = INT_CHAR | FLOAT_CHAR | IDENT_OTHER_CHAR | IDENT_RAW_CHAR; // [0-9] -const ABCDF: u8 = INT_CHAR | IDENT_FIRST_CHAR | IDENT_OTHER_CHAR | IDENT_RAW_CHAR; // [ABCDFabcdf] -const UNDER: u8 = INT_CHAR | FLOAT_CHAR | IDENT_FIRST_CHAR | IDENT_OTHER_CHAR | IDENT_RAW_CHAR; // [_] -const E____: u8 = INT_CHAR | FLOAT_CHAR | IDENT_FIRST_CHAR | IDENT_OTHER_CHAR | IDENT_RAW_CHAR; // [Ee] -const G2Z__: u8 = IDENT_FIRST_CHAR | IDENT_OTHER_CHAR | IDENT_RAW_CHAR; // [G-Zg-z] -const PUNCT: u8 = FLOAT_CHAR | IDENT_RAW_CHAR; // [\.+-] -const WS___: u8 = WHITESPACE_CHAR; // [\t\n\r ] -const _____: u8 = 0; // everything else - -// Table of encodings, for fast predicates. (Non-ASCII and special chars are -// shown with '·' in the comment.) -#[rustfmt::skip] -const ENCODINGS: [u8; 256] = [ -/* 0 1 2 3 4 5 6 7 8 9 */ -/* 0+: ·········· */ _____, _____, _____, _____, _____, _____, _____, _____, _____, WS___, -/* 10+: ·········· */ WS___, _____, _____, WS___, _____, _____, _____, _____, _____, _____, -/* 20+: ·········· */ _____, _____, _____, _____, _____, _____, _____, _____, _____, _____, -/* 30+: ·· !"#$%&' */ _____, _____, WS___, _____, _____, _____, _____, _____, _____, _____, -/* 40+: ()*+,-./01 */ _____, _____, _____, PUNCT, _____, PUNCT, PUNCT, _____, DIGIT, DIGIT, -/* 50+: 23456789:; */ DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, DIGIT, _____, _____, -/* 60+: <=>?@ABCDE */ _____, _____, _____, _____, _____, ABCDF, ABCDF, ABCDF, ABCDF, E____, -/* 70+: FGHIJKLMNO */ ABCDF, G2Z__, G2Z__, G2Z__, G2Z__, G2Z__, G2Z__, G2Z__, G2Z__, G2Z__, -/* 80+: PQRSTUVWZY */ G2Z__, G2Z__, G2Z__, G2Z__, G2Z__, G2Z__, G2Z__, G2Z__, G2Z__, G2Z__, -/* 90+: Z[\]^_`abc */ G2Z__, _____, _____, _____, _____, UNDER, _____, ABCDF, ABCDF, ABCDF, -/* 100+: defghijklm */ ABCDF, E____, ABCDF, G2Z__, G2Z__, G2Z__, G2Z__, G2Z__, G2Z__, G2Z__, -/* 110+: nopqrstuvw */ G2Z__, G2Z__, G2Z__, G2Z__, G2Z__, G2Z__, G2Z__, G2Z__, G2Z__, G2Z__, -/* 120+: xyz{|}~··· */ G2Z__, G2Z__, G2Z__, _____, _____, _____, _____, _____, _____, _____, -/* 130+: ·········· */ _____, _____, _____, _____, _____, _____, _____, _____, _____, _____, -/* 140+: ·········· */ _____, _____, _____, _____, _____, _____, _____, _____, _____, _____, -/* 150+: ·········· */ _____, _____, _____, _____, _____, _____, _____, _____, _____, _____, -/* 160+: ·········· */ _____, _____, _____, _____, _____, _____, _____, _____, _____, _____, -/* 170+: ·········· */ _____, _____, _____, _____, _____, _____, _____, _____, _____, _____, -/* 180+: ·········· */ _____, _____, _____, _____, _____, _____, _____, _____, _____, _____, -/* 190+: ·········· */ _____, _____, _____, _____, _____, _____, _____, _____, _____, _____, -/* 200+: ·········· */ _____, _____, _____, _____, _____, _____, _____, _____, _____, _____, -/* 210+: ·········· */ _____, _____, _____, _____, _____, _____, _____, _____, _____, _____, -/* 220+: ·········· */ _____, _____, _____, _____, _____, _____, _____, _____, _____, _____, -/* 230+: ·········· */ _____, _____, _____, _____, _____, _____, _____, _____, _____, _____, -/* 240+: ·········· */ _____, _____, _____, _____, _____, _____, _____, _____, _____, _____, -/* 250+: ·········· */ _____, _____, _____, _____, _____, _____ -]; - -const fn is_int_char(c: u8) -> bool { - ENCODINGS[c as usize] & INT_CHAR != 0 +const fn is_int_char(c: char) -> bool { + c.is_ascii_hexdigit() || c == '_' } -const fn is_float_char(c: u8) -> bool { - ENCODINGS[c as usize] & FLOAT_CHAR != 0 +const fn is_float_char(c: char) -> bool { + c.is_ascii_digit() || matches!(c, 'e' | 'E' | '.' | '+' | '-' | '_') } -pub const fn is_ident_first_char(c: u8) -> bool { - ENCODINGS[c as usize] & IDENT_FIRST_CHAR != 0 +pub fn is_ident_first_char(c: char) -> bool { + c == '_' || is_xid_start(c) } -pub const fn is_ident_other_char(c: u8) -> bool { - ENCODINGS[c as usize] & IDENT_OTHER_CHAR != 0 +pub fn is_ident_raw_char(c: char) -> bool { + matches!(c, '.' | '+' | '-') | is_xid_continue(c) } -pub const fn is_ident_raw_char(c: u8) -> bool { - ENCODINGS[c as usize] & IDENT_RAW_CHAR != 0 -} - -const fn is_whitespace_char(c: u8) -> bool { - ENCODINGS[c as usize] & WHITESPACE_CHAR != 0 -} - -#[derive(Clone, Debug, PartialEq)] -pub enum AnyNum { - F32(f32), - F64(f64), - I8(i8), - U8(u8), - I16(i16), - U16(u16), - I32(i32), - U32(u32), - I64(i64), - U64(u64), - #[cfg(feature = "integer128")] - I128(i128), - #[cfg(feature = "integer128")] - U128(u128), -} - -#[derive(Clone, Copy, Debug)] -pub struct Bytes<'a> { - /// Bits set according to the [`Extensions`] enum. - pub exts: Extensions, - bytes: &'a [u8], - cursor: Position, +pub const fn is_whitespace_char(c: char) -> bool { + matches!( + c, + ' ' | '\t' + | '\n' + | '\r' + | '\x0B' + | '\x0C' + | '\u{85}' + | '\u{200E}' + | '\u{200F}' + | '\u{2028}' + | '\u{2029}' + ) } #[cfg(feature = "integer128")] @@ -124,281 +54,452 @@ pub(crate) type LargeSInt = i128; #[cfg(not(feature = "integer128"))] pub(crate) type LargeSInt = i64; -impl<'a> Bytes<'a> { - pub fn new(bytes: &'a [u8]) -> SpannedResult { - let mut b = Bytes { +pub struct Parser<'a> { + /// Bits set according to the [`Extensions`] enum. + pub exts: Extensions, + src: &'a str, + cursor: ParserCursor, +} + +#[derive(Copy, Clone)] // GRCOV_EXCL_LINE +pub struct ParserCursor { + cursor: usize, + pre_ws_cursor: usize, + last_ws_len: usize, +} + +const WS_CURSOR_UNCLOSED_LINE: usize = usize::MAX; + +impl PartialEq for ParserCursor { + fn eq(&self, other: &Self) -> bool { + self.cursor == other.cursor + } +} + +impl PartialOrd for ParserCursor { + fn partial_cmp(&self, other: &Self) -> Option { + self.cursor.partial_cmp(&other.cursor) + } +} + +/// constructor and parsing utilities +impl<'a> Parser<'a> { + pub fn new(src: &'a str) -> SpannedResult { + let mut parser = Parser { exts: Extensions::empty(), - bytes, - cursor: Position { line: 1, col: 1 }, + src, + cursor: ParserCursor { + cursor: 0, + pre_ws_cursor: 0, + last_ws_len: 0, + }, }; - b.skip_ws().map_err(|e| b.span_error(e))?; + parser.skip_ws().map_err(|e| parser.span_error(e))?; // Loop over all extensions attributes loop { - let attribute = b.extensions().map_err(|e| b.span_error(e))?; + let attribute = parser.extensions().map_err(|e| parser.span_error(e))?; if attribute.is_empty() { break; } - b.exts |= attribute; - b.skip_ws().map_err(|e| b.span_error(e))?; + parser.exts |= attribute; + parser.skip_ws().map_err(|e| parser.span_error(e))?; } - Ok(b) + Ok(parser) + } + + fn set_cursor(&mut self, cursor: ParserCursor) { + self.cursor = cursor; } pub fn span_error(&self, code: Error) -> SpannedError { SpannedError { code, - position: self.cursor, + position: Position::from_src_end(&self.src[..self.cursor.cursor]), } } - pub fn advance(&mut self, bytes: usize) -> Result<()> { - for _ in 0..bytes { - self.advance_single()?; - } - - Ok(()) + pub fn advance_bytes(&mut self, bytes: usize) { + self.cursor.cursor += bytes; } - pub fn advance_single(&mut self) -> Result<()> { - if self.peek_or_eof()? == b'\n' { - self.cursor.line += 1; - self.cursor.col = 1; + pub fn next_char(&mut self) -> Result { + let c = self.peek_char_or_eof()?; + self.cursor.cursor += c.len_utf8(); + Ok(c) + } + + pub fn skip_next_char(&mut self) { + std::mem::drop(self.next_char()); + } + + pub fn peek_char(&self) -> Option { + self.src().chars().next() + } + + pub fn peek_char_or_eof(&self) -> Result { + self.peek_char().ok_or(Error::Eof) + } + + pub fn check_char(&self, c: char) -> bool { + self.src().starts_with(c) + } + + pub fn check_str(&self, s: &str) -> bool { + self.src().starts_with(s) + } + + pub fn src(&self) -> &'a str { + &self.src[self.cursor.cursor..] + } + + pub fn pre_ws_src(&self) -> &'a str { + &self.src[self.cursor.pre_ws_cursor..] + } + + pub fn consume_str(&mut self, s: &str) -> bool { + if self.check_str(s) { + self.advance_bytes(s.len()); + + true } else { - self.cursor.col += 1; + false } - - self.bytes = &self.bytes[1..]; - - Ok(()) } - fn any_integer(&mut self, sign: i8) -> Result { - let base = if self.peek() == Some(b'0') { - match self.bytes.get(1).copied() { - Some(b'x') => 16, - Some(b'b') => 2, - Some(b'o') => 8, - _ => 10, + pub fn consume_char(&mut self, c: char) -> bool { + if self.check_char(c) { + self.advance_bytes(c.len_utf8()); + + true + } else { + false + } + } + + fn consume_all(&mut self, all: &[&str]) -> Result { + all.iter() + .map(|elem| { + if self.consume_str(elem) { + self.skip_ws()?; + + Ok(true) + } else { + Ok(false) + } + }) + .try_fold(true, |acc, x| x.map(|x| x && acc)) + } + + pub fn expect_char(&mut self, expected: char, error: Error) -> Result<()> { + if self.consume_char(expected) { + Ok(()) + } else { + Err(error) + } + } + + #[must_use] + pub fn next_chars_while_len(&self, condition: fn(char) -> bool) -> usize { + self.next_chars_while_from_len(0, condition) + } + + #[must_use] + pub fn next_chars_while_from_len(&self, from: usize, condition: fn(char) -> bool) -> usize { + self.src()[from..] + .find(|c| !condition(c)) + .unwrap_or(self.src().len() - from) + } +} + +/// actual parsing of ron tokens +impl<'a> Parser<'a> { + fn parse_integer_digits( + &mut self, + s: &str, + base: u8, + f: fn(&mut T, u8) -> bool, + ) -> Result { + let mut num_acc = T::from_u8(0); + + for (i, c) in s.char_indices() { + if c == '_' { + continue; } - } else { - 10 + + if num_acc.checked_mul_ext(base) { + self.advance_bytes(s.len()); + return Err(Error::IntegerOutOfBounds); + } + + let digit = Self::decode_hex(c)?; + + if digit >= base { + self.advance_bytes(i); + return Err(Error::InvalidIntegerDigit { digit: c, base }); + } + + if f(&mut num_acc, digit) { + self.advance_bytes(s.len()); + return Err(Error::IntegerOutOfBounds); + } + } + + self.advance_bytes(s.len()); + + Ok(num_acc) + } + + fn parse_integer(&mut self, sign: i8) -> Result { + let base = match () { + () if self.consume_str("0b") => 2, + () if self.consume_str("0o") => 8, + () if self.consume_str("0x") => 16, + () => 10, }; - if base != 10 { - // If we have `0x45A` for example, - // cut it to `45A`. - let _ = self.advance(2); - } - - let num_bytes = self.next_bytes_contained_in(is_int_char); + let num_bytes = self.next_chars_while_len(is_int_char); if num_bytes == 0 { return Err(Error::ExpectedInteger); } - let s = unsafe { from_utf8_unchecked(&self.bytes[0..num_bytes]) }; - - if s.as_bytes()[0] == b'_' { + if self.check_char('_') { return Err(Error::UnderscoreAtBeginning); } - fn calc_num( - bytes: &Bytes, - s: &str, - base: u8, - mut f: impl FnMut(&mut T, u8) -> bool, - ) -> Result { - let mut num_acc = T::from_u8(0); + let s = &self.src()[..num_bytes]; - for &byte in s.as_bytes() { - if byte == b'_' { - continue; - } - - if num_acc.checked_mul_ext(base) { - return Err(Error::IntegerOutOfBounds); - } - - let digit = bytes.decode_hex(byte)?; - - if digit >= base { - return Err(Error::ExpectedInteger); - } - - if f(&mut num_acc, digit) { - return Err(Error::IntegerOutOfBounds); - } - } - - Ok(num_acc) - } - - let res = if sign > 0 { - calc_num(self, s, base, T::checked_add_ext) + if sign > 0 { + self.parse_integer_digits(s, base, T::checked_add_ext) } else { - calc_num(self, s, base, T::checked_sub_ext) - }; - - let _ = self.advance(num_bytes); - - res + self.parse_integer_digits(s, base, T::checked_sub_ext) + } } - pub fn any_num(&mut self) -> Result { - // We are not doing float comparisons here in the traditional sense. - // Instead, this code checks if a f64 fits inside an f32. - #[allow(clippy::float_cmp)] - fn any_float(f: f64) -> Result { - if f == f64::from(f as f32) { - Ok(AnyNum::F32(f as f32)) - } else { - Ok(AnyNum::F64(f)) + #[allow(clippy::too_many_lines)] + pub fn integer(&mut self) -> Result { + let src_backup = self.src(); + + let is_negative = match self.peek_char_or_eof()? { + '+' => { + self.skip_next_char(); + false } + '-' => { + self.skip_next_char(); + true + } + 'b' if self.consume_str("b'") => { + // Parse a byte literal + let byte = match self.next_char()? { + '\\' => match self.parse_escape(EscapeEncoding::Binary, true)? { + // we know that this byte is an ASCII character + EscapeCharacter::Ascii(b) => b, + EscapeCharacter::Utf8(_) => { + return Err(Error::InvalidEscape( + "Unexpected Unicode escape in byte literal", + )) + } + }, + b if b.is_ascii() => b as u8, + _ => return Err(Error::ExpectedByteLiteral), + }; + + if !self.consume_char('\'') { + return Err(Error::ExpectedByteLiteral); + } + + let bytes_ron = &src_backup[..src_backup.len() - self.src().len()]; + + return T::try_from_parsed_integer(ParsedInteger::U8(byte), bytes_ron); + } + _ => false, + }; + let sign = if is_negative { -1 } else { 1 }; + + let num_bytes = self.next_chars_while_len(is_int_char); + + if self.src()[num_bytes..].starts_with(['i', 'u']) { + let int_cursor = self.cursor; + self.advance_bytes(num_bytes); + + #[allow(clippy::never_loop)] + loop { + let (res, suffix_bytes) = if self.consume_ident("i8") { + let suffix_bytes = self.src(); + self.set_cursor(int_cursor); + ( + self.parse_integer::(sign).map(ParsedInteger::I8), + suffix_bytes, + ) + } else if self.consume_ident("i16") { + let suffix_bytes = self.src(); + self.set_cursor(int_cursor); + ( + self.parse_integer::(sign).map(ParsedInteger::I16), + suffix_bytes, + ) + } else if self.consume_ident("i32") { + let suffix_bytes = self.src(); + self.set_cursor(int_cursor); + ( + self.parse_integer::(sign).map(ParsedInteger::I32), + suffix_bytes, + ) + } else if self.consume_ident("i64") { + let suffix_bytes = self.src(); + self.set_cursor(int_cursor); + ( + self.parse_integer::(sign).map(ParsedInteger::I64), + suffix_bytes, + ) + } else if self.consume_ident("u8") { + let suffix_bytes = self.src(); + self.set_cursor(int_cursor); + ( + self.parse_integer::(sign).map(ParsedInteger::U8), + suffix_bytes, + ) + } else if self.consume_ident("u16") { + let suffix_bytes = self.src(); + self.set_cursor(int_cursor); + ( + self.parse_integer::(sign).map(ParsedInteger::U16), + suffix_bytes, + ) + } else if self.consume_ident("u32") { + let suffix_bytes = self.src(); + self.set_cursor(int_cursor); + ( + self.parse_integer::(sign).map(ParsedInteger::U32), + suffix_bytes, + ) + } else if self.consume_ident("u64") { + let suffix_bytes = self.src(); + self.set_cursor(int_cursor); + ( + self.parse_integer::(sign).map(ParsedInteger::U64), + suffix_bytes, + ) + } else { + #[cfg(feature = "integer128")] + if self.consume_ident("i128") { + let suffix_bytes = self.src(); + self.set_cursor(int_cursor); + ( + self.parse_integer::(sign).map(ParsedInteger::I128), + suffix_bytes, + ) + } else if self.consume_ident("u128") { + let suffix_bytes = self.src(); + self.set_cursor(int_cursor); + ( + self.parse_integer::(sign).map(ParsedInteger::U128), + suffix_bytes, + ) + } else { + break; + } + #[cfg(not(feature = "integer128"))] + { + break; + } + }; + + if !matches!( + &res, + Err(Error::UnderscoreAtBeginning | Error::InvalidIntegerDigit { .. }) + ) { + // Advance past the number suffix + self.skip_identifier(); + } + + let integer_ron = &src_backup[..src_backup.len() - suffix_bytes.len()]; + + return res.and_then(|parsed| T::try_from_parsed_integer(parsed, integer_ron)); + } + + self.set_cursor(int_cursor); } - let bytes_backup = self.bytes; + T::parse(self, sign) + } - let first_byte = self.peek_or_eof()?; - let is_signed = first_byte == b'-' || first_byte == b'+'; - let is_float = self.next_bytes_is_float(); + pub fn any_number(&mut self) -> Result { + if self.next_bytes_is_float() { + return match self.float::()? { + ParsedFloat::F32(v) => Ok(Number::F32(v.into())), + ParsedFloat::F64(v) => Ok(Number::F64(v.into())), + }; + } - if is_float { - let f = self.float::()?; + let backup_cursor = self.cursor; - any_float(f) - } else { - let max_u8 = LargeUInt::from(std::u8::MAX); - let max_u16 = LargeUInt::from(std::u16::MAX); - let max_u32 = LargeUInt::from(std::u32::MAX); - #[cfg_attr(not(feature = "integer128"), allow(clippy::useless_conversion))] - let max_u64 = LargeUInt::from(std::u64::MAX); - - let min_i8 = LargeSInt::from(std::i8::MIN); - let max_i8 = LargeSInt::from(std::i8::MAX); - let min_i16 = LargeSInt::from(std::i16::MIN); - let max_i16 = LargeSInt::from(std::i16::MAX); - let min_i32 = LargeSInt::from(std::i32::MIN); - let max_i32 = LargeSInt::from(std::i32::MAX); - #[cfg_attr(not(feature = "integer128"), allow(clippy::useless_conversion))] - let min_i64 = LargeSInt::from(std::i64::MIN); - #[cfg_attr(not(feature = "integer128"), allow(clippy::useless_conversion))] - let max_i64 = LargeSInt::from(std::i64::MAX); - - if is_signed { - match self.signed_integer::() { - Ok(x) => { - if x >= min_i8 && x <= max_i8 { - Ok(AnyNum::I8(x as i8)) - } else if x >= min_i16 && x <= max_i16 { - Ok(AnyNum::I16(x as i16)) - } else if x >= min_i32 && x <= max_i32 { - Ok(AnyNum::I32(x as i32)) - } else if x >= min_i64 && x <= max_i64 { - Ok(AnyNum::I64(x as i64)) - } else { - #[cfg(feature = "integer128")] - { - Ok(AnyNum::I128(x)) - } - #[cfg(not(feature = "integer128"))] - { - Ok(AnyNum::I64(x)) - } - } - } - Err(_) => { - self.bytes = bytes_backup; - - any_float(self.float::()?) - } + let (integer_err, integer_cursor) = match self.integer::() { + Ok(integer) => { + return match integer { + ParsedInteger::I8(v) => Ok(Number::I8(v)), + ParsedInteger::I16(v) => Ok(Number::I16(v)), + ParsedInteger::I32(v) => Ok(Number::I32(v)), + ParsedInteger::I64(v) => Ok(Number::I64(v)), + #[cfg(feature = "integer128")] + ParsedInteger::I128(v) => Ok(Number::I128(v)), + ParsedInteger::U8(v) => Ok(Number::U8(v)), + ParsedInteger::U16(v) => Ok(Number::U16(v)), + ParsedInteger::U32(v) => Ok(Number::U32(v)), + ParsedInteger::U64(v) => Ok(Number::U64(v)), + #[cfg(feature = "integer128")] + ParsedInteger::U128(v) => Ok(Number::U128(v)), } - } else { - match self.unsigned_integer::() { - Ok(x) => { - if x <= max_u8 { - Ok(AnyNum::U8(x as u8)) - } else if x <= max_u16 { - Ok(AnyNum::U16(x as u16)) - } else if x <= max_u32 { - Ok(AnyNum::U32(x as u32)) - } else if x <= max_u64 { - Ok(AnyNum::U64(x as u64)) - } else { - #[cfg(feature = "integer128")] - { - Ok(AnyNum::U128(x)) - } - #[cfg(not(feature = "integer128"))] - { - Ok(AnyNum::U64(x)) - } - } - } - Err(_) => { - self.bytes = bytes_backup; + } + Err(err) => (err, self.cursor), + }; - any_float(self.float::()?) - } - } + self.set_cursor(backup_cursor); + + // Fall-back to parse an out-of-range integer as a float + match self.float::() { + Ok(ParsedFloat::F32(v)) if self.cursor >= integer_cursor => Ok(Number::F32(v.into())), + Ok(ParsedFloat::F64(v)) if self.cursor >= integer_cursor => Ok(Number::F64(v.into())), + _ => { + // Return the more precise integer error + self.set_cursor(integer_cursor); + Err(integer_err) } } } pub fn bool(&mut self) -> Result { - if self.consume("true") { + if self.consume_ident("true") { Ok(true) - } else if self.consume("false") { + } else if self.consume_ident("false") { Ok(false) } else { Err(Error::ExpectedBoolean) } } - pub fn bytes(&self) -> &[u8] { - self.bytes - } - pub fn char(&mut self) -> Result { - if !self.consume("'") { - return Err(Error::ExpectedChar); - } + self.expect_char('\'', Error::ExpectedChar)?; - let c = self.peek_or_eof()?; + let c = self.next_char()?; - let c = if c == b'\\' { - let _ = self.advance(1); - - self.parse_escape()? - } else { - // Check where the end of the char (') is and try to - // interpret the rest as UTF-8 - - let max = self.bytes.len().min(5); - let pos: usize = self.bytes[..max] - .iter() - .position(|&x| x == b'\'') - .ok_or(Error::ExpectedChar)?; - let s = from_utf8(&self.bytes[0..pos]).map_err(Error::from)?; - let mut chars = s.chars(); - - let first = chars.next().ok_or(Error::ExpectedChar)?; - if chars.next().is_some() { - return Err(Error::ExpectedChar); + let c = if c == '\\' { + match self.parse_escape(EscapeEncoding::Utf8, true)? { + // we know that this byte is an ASCII character + EscapeCharacter::Ascii(b) => char::from(b), + EscapeCharacter::Utf8(c) => c, } - - let _ = self.advance(pos); - - first + } else { + c }; - if !self.consume("'") { - return Err(Error::ExpectedChar); - } + self.expect_char('\'', Error::ExpectedChar)?; Ok(c) } @@ -406,7 +507,7 @@ impl<'a> Bytes<'a> { pub fn comma(&mut self) -> Result { self.skip_ws()?; - if self.consume(",") { + if self.consume_char(',') { self.skip_ws()?; Ok(true) @@ -418,33 +519,146 @@ impl<'a> Bytes<'a> { /// Only returns true if the char after `ident` cannot belong /// to an identifier. pub fn check_ident(&mut self, ident: &str) -> bool { - self.test_for(ident) && !self.check_ident_other_char(ident.len()) + self.check_str(ident) && !self.check_ident_other_char(ident.len()) } fn check_ident_other_char(&self, index: usize) -> bool { - self.bytes - .get(index) - .map_or(false, |&b| is_ident_other_char(b)) + self.src()[index..] + .chars() + .next() + .map_or(false, is_xid_continue) } - /// Should only be used on a working copy - pub fn check_tuple_struct(mut self) -> Result { - if self.identifier().is_err() { - // if there's no field ident, this is a tuple struct - return Ok(true); + /// Check which type of struct we are currently parsing. The parsing state + /// is only changed in case of an error, to provide a better position. + /// + /// [`NewtypeMode::NoParensMeanUnit`] detects (tuple) structs by a leading + /// opening bracket and reports a unit struct otherwise. + /// [`NewtypeMode::InsideNewtype`] skips an initial check for unit structs, + /// and means that any leading opening bracket is not considered to open + /// a (tuple) struct but to be part of the structs inner contents. + /// + /// [`TupleMode::ImpreciseTupleOrNewtype`] only performs a cheap, O(1), + /// single-identifier lookahead check to distinguish tuple structs from + /// non-tuple structs. + /// [`TupleMode::DifferentiateNewtype`] performs an expensive, O(N), look- + /// ahead over the entire next value tree, which can span the entirety of + /// the remaining document in the worst case. + pub fn check_struct_type( + &mut self, + newtype: NewtypeMode, + tuple: TupleMode, + ) -> Result { + fn check_struct_type_inner( + parser: &mut Parser, + newtype: NewtypeMode, + tuple: TupleMode, + ) -> Result { + if matches!(newtype, NewtypeMode::NoParensMeanUnit) && !parser.consume_char('(') { + return Ok(StructType::Unit); + } + + parser.skip_ws()?; + + // Check for `Ident()`, which could be + // - a zero-field struct or tuple (variant) + // - an unwrapped newtype around a unit + if matches!(newtype, NewtypeMode::NoParensMeanUnit) && parser.check_char(')') { + return Ok(StructType::EmptyTuple); + } + + if parser.skip_identifier().is_some() { + parser.skip_ws()?; + + match parser.peek_char() { + // Definitely a struct with named fields + Some(':') => return Ok(StructType::Named), + // Definitely a tuple-like struct with fields + Some(',') => { + parser.skip_next_char(); + parser.skip_ws()?; + if parser.check_char(')') { + // A one-element tuple could be a newtype + return Ok(StructType::NewtypeTuple); + } + // Definitely a tuple struct with more than one field + return Ok(StructType::NonNewtypeTuple); + } + // Either a newtype or a tuple struct + Some(')') => return Ok(StructType::NewtypeTuple), + // Something else, let's investigate further + Some(_) | None => (), + }; + } + + if matches!(tuple, TupleMode::ImpreciseTupleOrNewtype) { + return Ok(StructType::AnyTuple); + } + + let mut braces = 1_usize; + let mut more_than_one = false; + + // Skip ahead to see if the value is followed by another value + while braces > 0 { + // Skip spurious braces in comments, strings, and characters + parser.skip_ws()?; + let cursor_backup = parser.cursor; + if parser.char().is_err() { + parser.set_cursor(cursor_backup); + } + let cursor_backup = parser.cursor; + match parser.string() { + Ok(_) => (), + // prevent quadratic complexity backtracking for unterminated string + Err(err @ (Error::ExpectedStringEnd | Error::Eof)) => return Err(err), + Err(_) => parser.set_cursor(cursor_backup), + } + let cursor_backup = parser.cursor; + // we have already checked for strings, which subsume base64 byte strings + match parser.byte_string_no_base64() { + Ok(_) => (), + // prevent quadratic complexity backtracking for unterminated byte string + Err(err @ (Error::ExpectedStringEnd | Error::Eof)) => return Err(err), + Err(_) => parser.set_cursor(cursor_backup), + } + + let c = parser.next_char()?; + if matches!(c, '(' | '[' | '{') { + braces += 1; + } else if matches!(c, ')' | ']' | '}') { + braces -= 1; + } else if c == ',' && braces == 1 { + parser.skip_ws()?; + more_than_one = !parser.check_char(')'); + break; + } + } + + if more_than_one { + Ok(StructType::NonNewtypeTuple) + } else { + Ok(StructType::NewtypeTuple) + } } - self.skip_ws()?; + // Create a temporary working copy + let backup_cursor = self.cursor; - // if there is no colon after the ident, this can only be a unit struct - self.eat_byte().map(|c| c != b':') + let result = check_struct_type_inner(self, newtype, tuple); + + if result.is_ok() { + // Revert the parser to before the struct type check + self.set_cursor(backup_cursor); + } + + result } /// Only returns true if the char after `ident` cannot belong /// to an identifier. pub fn consume_ident(&mut self, ident: &str) -> bool { if self.check_ident(ident) { - let _ = self.advance(ident.len()); + self.advance_bytes(ident.len()); true } else { @@ -454,17 +668,25 @@ impl<'a> Bytes<'a> { pub fn consume_struct_name(&mut self, ident: &'static str) -> Result { if self.check_ident("") { + if self.exts.contains(Extensions::EXPLICIT_STRUCT_NAMES) { + return Err(Error::ExpectedStructName(ident.to_string())); + } + return Ok(false); } let found_ident = match self.identifier() { - Ok(maybe_ident) => std::str::from_utf8(maybe_ident)?, + Ok(maybe_ident) => maybe_ident, Err(Error::SuggestRawIdentifier(found_ident)) if found_ident == ident => { return Err(Error::SuggestRawIdentifier(found_ident)) } Err(_) => return Err(Error::ExpectedNamedStructLike(ident)), }; + if ident.is_empty() { + return Err(Error::ExpectedNamedStructLike(ident)); + } + if found_ident != ident { return Err(Error::ExpectedDifferentStructName { expected: ident, @@ -475,45 +697,9 @@ impl<'a> Bytes<'a> { Ok(true) } - pub fn consume(&mut self, s: &str) -> bool { - if self.test_for(s) { - let _ = self.advance(s.len()); - - true - } else { - false - } - } - - fn consume_all(&mut self, all: &[&str]) -> Result { - all.iter() - .map(|elem| { - if self.consume(elem) { - self.skip_ws()?; - - Ok(true) - } else { - Ok(false) - } - }) - .fold(Ok(true), |acc, x| acc.and_then(|val| x.map(|x| x && val))) - } - - pub fn eat_byte(&mut self) -> Result { - let peek = self.peek_or_eof()?; - let _ = self.advance_single(); - - Ok(peek) - } - - pub fn expect_byte(&mut self, byte: u8, error: Error) -> Result<()> { - self.eat_byte() - .and_then(|b| if b == byte { Ok(()) } else { Err(error) }) - } - /// Returns the extensions bit mask. fn extensions(&mut self) -> Result { - if self.peek() != Some(b'#') { + if !self.check_char('#') { return Ok(Extensions::empty()); } @@ -526,9 +712,8 @@ impl<'a> Bytes<'a> { loop { let ident = self.identifier()?; - let extension = Extensions::from_ident(ident).ok_or_else(|| { - Error::NoSuchExtension(String::from_utf8_lossy(ident).into_owned()) - })?; + let extension = Extensions::from_ident(ident) + .ok_or_else(|| Error::NoSuchExtension(ident.into()))?; extensions |= extension; @@ -556,166 +741,359 @@ impl<'a> Bytes<'a> { } } - pub fn float(&mut self) -> Result - where - T: FromStr, - { - for literal in &["inf", "+inf", "-inf", "NaN", "+NaN", "-NaN"] { + pub fn float(&mut self) -> Result { + const F32_SUFFIX: &str = "f32"; + const F64_SUFFIX: &str = "f64"; + + for (literal, value_f32, value_f64) in &[ + ("inf", f32::INFINITY, f64::INFINITY), + ("+inf", f32::INFINITY, f64::INFINITY), + ("-inf", f32::NEG_INFINITY, f64::NEG_INFINITY), + ("NaN", f32::NAN, f64::NAN), + ("+NaN", f32::NAN, f64::NAN), + ("-NaN", -f32::NAN, -f64::NAN), + ] { if self.consume_ident(literal) { - return FromStr::from_str(literal).map_err(|_| unreachable!()); // must not fail + return T::parse(literal); + } + + if let Some(suffix) = self.src().strip_prefix(literal) { + if let Some(post_suffix) = suffix.strip_prefix(F32_SUFFIX) { + if !post_suffix.chars().next().map_or(false, is_xid_continue) { + let float_ron = &self.src()[..literal.len() + F32_SUFFIX.len()]; + self.advance_bytes(literal.len() + F32_SUFFIX.len()); + return T::try_from_parsed_float(ParsedFloat::F32(*value_f32), float_ron); + } + } + + if let Some(post_suffix) = suffix.strip_prefix(F64_SUFFIX) { + if !post_suffix.chars().next().map_or(false, is_xid_continue) { + let float_ron = &self.src()[..literal.len() + F64_SUFFIX.len()]; + self.advance_bytes(literal.len() + F64_SUFFIX.len()); + return T::try_from_parsed_float(ParsedFloat::F64(*value_f64), float_ron); + } + } } } - let num_bytes = self.next_bytes_contained_in(is_float_char); + let num_bytes = self.next_chars_while_len(is_float_char); - // Since `rustc` allows `1_0.0_1`, lint against underscores in floats - if let Some(err_bytes) = self.bytes[0..num_bytes].iter().position(|b| *b == b'_') { - let _ = self.advance(err_bytes); - - return Err(Error::FloatUnderscore); + if num_bytes == 0 { + return Err(Error::ExpectedFloat); } - let s = unsafe { from_utf8_unchecked(&self.bytes[0..num_bytes]) }; - let res = FromStr::from_str(s).map_err(|_| Error::ExpectedFloat); + if self.check_char('_') { + return Err(Error::UnderscoreAtBeginning); + } - let _ = self.advance(num_bytes); + let mut f = String::with_capacity(num_bytes); + let mut allow_underscore = false; - res + for (i, c) in self.src()[..num_bytes].char_indices() { + match c { + '_' if allow_underscore => continue, + '_' => { + self.advance_bytes(i); + return Err(Error::FloatUnderscore); + } + '0'..='9' | 'e' | 'E' => allow_underscore = true, + '.' => allow_underscore = false, + _ => (), + } + + // we know that the byte is an ASCII character here + f.push(c); + } + + if self.src()[num_bytes..].starts_with('f') { + let backup_cursor = self.cursor; + self.advance_bytes(num_bytes); + + #[allow(clippy::never_loop)] + loop { + let res = if self.consume_ident(F32_SUFFIX) { + f32::from_str(&f).map(ParsedFloat::F32) + } else if self.consume_ident(F64_SUFFIX) { + f64::from_str(&f).map(ParsedFloat::F64) + } else { + break; + }; + + let parsed = if let Ok(parsed) = res { + parsed + } else { + self.set_cursor(backup_cursor); + return Err(Error::ExpectedFloat); + }; + + let float_ron = &self.src[backup_cursor.cursor..self.cursor.cursor]; + + return T::try_from_parsed_float(parsed, float_ron); + } + + self.set_cursor(backup_cursor); + } + + let value = T::parse(&f)?; + + self.advance_bytes(num_bytes); + + Ok(value) } - pub fn identifier(&mut self) -> Result<&'a [u8]> { - let next = self.peek_or_eof()?; - if !is_ident_first_char(next) { - if is_ident_raw_char(next) { - let ident_bytes = &self.bytes[..self.next_bytes_contained_in(is_ident_raw_char)]; + pub fn skip_identifier(&mut self) -> Option<&'a str> { + #[allow(clippy::nonminimal_bool)] + if self.check_str("b\"") // byte string + || self.check_str("b'") // byte literal + || self.check_str("br#") // raw byte string + || self.check_str("br\"") // raw byte string + || self.check_str("r\"") // raw string + || self.check_str("r#\"") // raw string + || self.check_str("r##") // raw string + || false + { + return None; + } - if let Ok(ident) = std::str::from_utf8(ident_bytes) { - return Err(Error::SuggestRawIdentifier(String::from(ident))); - } + if self.check_str("r#") { + // maybe a raw identifier + let len = self.next_chars_while_from_len(2, is_ident_raw_char); + if len > 0 { + let ident = &self.src()[2..2 + len]; + self.advance_bytes(2 + len); + return Some(ident); + } + return None; + } + + if let Some(c) = self.peek_char() { + // maybe a normal identifier + if is_ident_first_char(c) { + let len = + c.len_utf8() + self.next_chars_while_from_len(c.len_utf8(), is_xid_continue); + let ident = &self.src()[..len]; + self.advance_bytes(len); + return Some(ident); + } + } + + None + } + + pub fn identifier(&mut self) -> Result<&'a str> { + let first = self.peek_char_or_eof()?; + if !is_ident_first_char(first) { + if is_ident_raw_char(first) { + let ident_bytes = self.next_chars_while_len(is_ident_raw_char); + return Err(Error::SuggestRawIdentifier( + self.src()[..ident_bytes].into(), + )); } return Err(Error::ExpectedIdentifier); } - // If the next two bytes signify the start of a raw string literal, - // return an error. - let length = if next == b'r' { - match self.bytes.get(1).ok_or(Error::Eof)? { - b'"' => return Err(Error::ExpectedIdentifier), - b'#' => { - let after_next = self.bytes.get(2).copied().unwrap_or_default(); - // Note: it's important to check this before advancing forward, so that - // the value-type deserializer can fall back to parsing it differently. - if !is_ident_raw_char(after_next) { - return Err(Error::ExpectedIdentifier); - } - // skip "r#" - let _ = self.advance(2); - self.next_bytes_contained_in(is_ident_raw_char) - } - _ => { - let std_ident_length = self.next_bytes_contained_in(is_ident_other_char); - let raw_ident_length = self.next_bytes_contained_in(is_ident_raw_char); + // If the next 2-3 bytes signify the start of a (raw) (byte) string + // literal, return an error. + #[allow(clippy::nonminimal_bool)] + if self.check_str("b\"") // byte string + || self.check_str("b'") // byte literal + || self.check_str("br#") // raw byte string + || self.check_str("br\"") // raw byte string + || self.check_str("r\"") // raw string + || self.check_str("r#\"") // raw string + || self.check_str("r##") // raw string + || false + { + return Err(Error::ExpectedIdentifier); + } - if raw_ident_length > std_ident_length { - if let Ok(ident) = std::str::from_utf8(&self.bytes[..raw_ident_length]) { - return Err(Error::SuggestRawIdentifier(String::from(ident))); - } - } + let length = if self.check_str("r#") { + let cursor_backup = self.cursor; - std_ident_length - } + self.advance_bytes(2); + + // Note: it's important to check this before advancing forward, so that + // the value-type deserializer can fall back to parsing it differently. + if !matches!(self.peek_char(), Some(c) if is_ident_raw_char(c)) { + self.set_cursor(cursor_backup); + return Err(Error::ExpectedIdentifier); } - } else { - let std_ident_length = self.next_bytes_contained_in(is_ident_other_char); - let raw_ident_length = self.next_bytes_contained_in(is_ident_raw_char); + + self.next_chars_while_len(is_ident_raw_char) + } else if first == 'r' { + let std_ident_length = self.next_chars_while_len(is_xid_continue); + let raw_ident_length = self.next_chars_while_len(is_ident_raw_char); if raw_ident_length > std_ident_length { - if let Ok(ident) = std::str::from_utf8(&self.bytes[..raw_ident_length]) { - return Err(Error::SuggestRawIdentifier(String::from(ident))); - } + return Err(Error::SuggestRawIdentifier( + self.src()[..raw_ident_length].into(), + )); + } + + std_ident_length + } else { + let std_ident_length = first.len_utf8() + + self.next_chars_while_from_len(first.len_utf8(), is_xid_continue); + let raw_ident_length = self.next_chars_while_len(is_ident_raw_char); + + if raw_ident_length > std_ident_length { + return Err(Error::SuggestRawIdentifier( + self.src()[..raw_ident_length].into(), + )); } std_ident_length }; - let ident = &self.bytes[..length]; - let _ = self.advance(length); + let ident = &self.src()[..length]; + self.advance_bytes(length); Ok(ident) } - pub fn next_bytes_contained_in(&self, allowed: fn(u8) -> bool) -> usize { - self.bytes.iter().take_while(|&&b| allowed(b)).count() - } - - pub fn next_bytes_is_float(&self) -> bool { - if let Some(byte) = self.peek() { - let skip = match byte { - b'+' | b'-' => 1, + pub fn next_bytes_is_float(&mut self) -> bool { + if let Some(c) = self.peek_char() { + let skip = match c { + '+' | '-' => 1, _ => 0, }; - let flen = self - .bytes - .iter() - .skip(skip) - .take_while(|&&b| is_float_char(b)) - .count(); - let ilen = self - .bytes - .iter() - .skip(skip) - .take_while(|&&b| is_int_char(b)) - .count(); - flen > ilen + let valid_float_len = self.next_chars_while_from_len(skip, is_float_char); + let valid_int_len = self.next_chars_while_from_len(skip, is_int_char); + valid_float_len > valid_int_len } else { false } } pub fn skip_ws(&mut self) -> Result<()> { - loop { - while self.peek().map_or(false, is_whitespace_char) { - let _ = self.advance_single(); - } + if (self.cursor.last_ws_len != WS_CURSOR_UNCLOSED_LINE) + && ((self.cursor.pre_ws_cursor + self.cursor.last_ws_len) < self.cursor.cursor) + { + // the last whitespace is disjoint from this one, we need to track a new one + self.cursor.pre_ws_cursor = self.cursor.cursor; + } - if !self.skip_comment()? { - return Ok(()); + if self.src().is_empty() { + return Ok(()); + } + + loop { + self.advance_bytes(self.next_chars_while_len(is_whitespace_char)); + + match self.skip_comment()? { + None => break, + Some(Comment::UnclosedLine) => { + self.cursor.last_ws_len = WS_CURSOR_UNCLOSED_LINE; + return Ok(()); + } + Some(Comment::ClosedLine | Comment::Block) => continue, } } + + self.cursor.last_ws_len = self.cursor.cursor - self.cursor.pre_ws_cursor; + + Ok(()) + } + + pub fn has_unclosed_line_comment(&self) -> bool { + self.src().is_empty() && self.cursor.last_ws_len == WS_CURSOR_UNCLOSED_LINE + } + + pub fn byte_string(&mut self) -> Result> { + fn expected_byte_string_found_base64( + base64_str: &ParsedStr, + byte_str: &ParsedByteStr, + ) -> Error { + let byte_str = match &byte_str { + ParsedByteStr::Allocated(b) => b.as_slice(), + ParsedByteStr::Slice(b) => b, + } + .iter() + .flat_map(|c| std::ascii::escape_default(*c)) + .map(char::from) + .collect::(); + let base64_str = match &base64_str { + ParsedStr::Allocated(s) => s.as_str(), + ParsedStr::Slice(s) => s, + }; + + Error::InvalidValueForType { + expected: format!("the Rusty byte string b\"{}\"", byte_str), + found: format!("the ambiguous base64 string {:?}", base64_str), + } + } + + if self.consume_char('"') { + let base64_str = self.escaped_string()?; + let base64_result = ParsedByteStr::try_from_base64(&base64_str); + + if cfg!(not(test)) { + // FIXME @juntyr: remove in v0.10 + #[allow(deprecated)] + base64_result.map_err(Error::Base64Error) + } else { + match base64_result { + // FIXME @juntyr: enable in v0.10 + Ok(byte_str) => Err(expected_byte_string_found_base64(&base64_str, &byte_str)), + Err(_) => Err(Error::ExpectedByteString), + } + } + } else if self.consume_char('r') { + let base64_str = self.raw_string()?; + let base64_result = ParsedByteStr::try_from_base64(&base64_str); + + if cfg!(not(test)) { + // FIXME @juntyr: remove in v0.10 + #[allow(deprecated)] + base64_result.map_err(Error::Base64Error) + } else { + match base64_result { + // FIXME @juntyr: enable in v0.10 + Ok(byte_str) => Err(expected_byte_string_found_base64(&base64_str, &byte_str)), + Err(_) => Err(Error::ExpectedByteString), + } + } + } else { + self.byte_string_no_base64() + } } - pub fn peek(&self) -> Option { - self.bytes.first().copied() + pub fn byte_string_no_base64(&mut self) -> Result> { + if self.consume_str("b\"") { + self.escaped_byte_string() + } else if self.consume_str("br") { + self.raw_byte_string() + } else { + Err(Error::ExpectedByteString) + } } - pub fn peek_or_eof(&self) -> Result { - self.bytes.first().copied().ok_or(Error::Eof) + fn escaped_byte_string(&mut self) -> Result> { + match self.escaped_byte_buf(EscapeEncoding::Binary) { + Ok((bytes, advance)) => { + self.advance_bytes(advance); + Ok(bytes) + } + Err(err) => Err(err), + } } - pub fn signed_integer(&mut self) -> Result - where - T: Num, - { - match self.peek_or_eof()? { - b'+' => { - let _ = self.advance_single(); - - self.any_integer(1) + fn raw_byte_string(&mut self) -> Result> { + match self.raw_byte_buf() { + Ok((bytes, advance)) => { + self.advance_bytes(advance); + Ok(bytes) } - b'-' => { - let _ = self.advance_single(); - - self.any_integer(-1) - } - _ => self.any_integer(1), + Err(Error::ExpectedString) => Err(Error::ExpectedByteString), + Err(err) => Err(err), } } pub fn string(&mut self) -> Result> { - if self.consume("\"") { + if self.consume_char('"') { self.escaped_string() - } else if self.consume("r") { + } else if self.consume_char('r') { self.raw_string() } else { Err(Error::ExpectedString) @@ -723,100 +1101,96 @@ impl<'a> Bytes<'a> { } fn escaped_string(&mut self) -> Result> { - use std::iter::repeat; - - let (i, end_or_escape) = self - .bytes - .iter() - .enumerate() - .find(|&(_, &b)| b == b'\\' || b == b'"') - .ok_or(Error::ExpectedStringEnd)?; - - if *end_or_escape == b'"' { - let s = from_utf8(&self.bytes[..i]).map_err(Error::from)?; - - // Advance by the number of bytes of the string - // + 1 for the `"`. - let _ = self.advance(i + 1); - - Ok(ParsedStr::Slice(s)) - } else { - let mut i = i; - let mut s: Vec<_> = self.bytes[..i].to_vec(); - - loop { - let _ = self.advance(i + 1); - let character = self.parse_escape()?; - match character.len_utf8() { - 1 => s.push(character as u8), - len => { - let start = s.len(); - s.extend(repeat(0).take(len)); - character.encode_utf8(&mut s[start..]); - } - } - - let (new_i, end_or_escape) = self - .bytes - .iter() - .enumerate() - .find(|&(_, &b)| b == b'\\' || b == b'"') - .ok_or(Error::ExpectedStringEnd)?; - - i = new_i; - s.extend_from_slice(&self.bytes[..i]); - - if *end_or_escape == b'"' { - let _ = self.advance(i + 1); - - let s = String::from_utf8(s).map_err(Error::from)?; - break Ok(ParsedStr::Allocated(s)); - } + match self.escaped_byte_buf(EscapeEncoding::Utf8) { + Ok((bytes, advance)) => { + let string = ParsedStr::try_from_bytes(bytes).map_err(Error::from)?; + self.advance_bytes(advance); + Ok(string) } + Err(err) => Err(err), } } fn raw_string(&mut self) -> Result> { - let num_hashes = self.bytes.iter().take_while(|&&b| b == b'#').count(); - let hashes = &self.bytes[..num_hashes]; - let _ = self.advance(num_hashes); - - if !self.consume("\"") { - return Err(Error::ExpectedString); + match self.raw_byte_buf() { + Ok((bytes, advance)) => { + let string = ParsedStr::try_from_bytes(bytes).map_err(Error::from)?; + self.advance_bytes(advance); + Ok(string) + } + Err(err) => Err(err), } + } - let ending = [&[b'"'], hashes].concat(); - let i = self - .bytes - .windows(num_hashes + 1) - .position(|window| window == ending.as_slice()) - .ok_or(Error::ExpectedStringEnd)?; + fn escaped_byte_buf(&mut self, encoding: EscapeEncoding) -> Result<(ParsedByteStr<'a>, usize)> { + // Checking for '"' and '\\' separately is faster than searching for both at the same time + let str_end = self.src().find('"').ok_or(Error::ExpectedStringEnd)?; + let escape = self.src()[..str_end].find('\\'); - let s = from_utf8(&self.bytes[..i]).map_err(Error::from)?; + if let Some(escape) = escape { + // Now check if escaping is used inside the string + let mut i = escape; + let mut s = self.src().as_bytes()[..i].to_vec(); - // Advance by the number of bytes of the string + loop { + self.advance_bytes(i + 1); + + match self.parse_escape(encoding, false)? { + EscapeCharacter::Ascii(c) => s.push(c), + EscapeCharacter::Utf8(c) => match c.len_utf8() { + 1 => s.push(c as u8), + len => { + let start = s.len(); + s.extend(std::iter::repeat(0).take(len)); + c.encode_utf8(&mut s[start..]); + } + }, + } + + // Checking for '"' and '\\' separately is faster than searching for both at the same time + let new_str_end = self.src().find('"').ok_or(Error::ExpectedStringEnd)?; + let new_escape = self.src()[..new_str_end].find('\\'); + + if let Some(new_escape) = new_escape { + s.extend_from_slice(&self.src().as_bytes()[..new_escape]); + i = new_escape; + } else { + s.extend_from_slice(&self.src().as_bytes()[..new_str_end]); + // Advance to the end of the string + 1 for the `"`. + break Ok((ParsedByteStr::Allocated(s), new_str_end + 1)); + } + } + } else { + let s = &self.src().as_bytes()[..str_end]; + + // Advance by the number of bytes of the string + 1 for the `"`. + Ok((ParsedByteStr::Slice(s), str_end + 1)) + } + } + + fn raw_byte_buf(&mut self) -> Result<(ParsedByteStr<'a>, usize)> { + let num_hashes = self.next_chars_while_len(|c| c == '#'); + let hashes = &self.src()[..num_hashes]; + self.advance_bytes(num_hashes); + + self.expect_char('"', Error::ExpectedString)?; + + let ending = ["\"", hashes].concat(); + let i = self.src().find(&ending).ok_or(Error::ExpectedStringEnd)?; + + let s = &self.src().as_bytes()[..i]; + + // Advance by the number of bytes of the byte string // + `num_hashes` + 1 for the `"`. - let _ = self.advance(i + num_hashes + 1); - - Ok(ParsedStr::Slice(s)) - } - - fn test_for(&self, s: &str) -> bool { - s.bytes() - .enumerate() - .all(|(i, b)| self.bytes.get(i).map_or(false, |t| *t == b)) - } - - pub fn unsigned_integer(&mut self) -> Result { - self.any_integer(1) + Ok((ParsedByteStr::Slice(s), i + num_hashes + 1)) } fn decode_ascii_escape(&mut self) -> Result { let mut n = 0; for _ in 0..2 { n <<= 4; - let byte = self.eat_byte()?; - let decoded = self.decode_hex(byte)?; + let byte = self.next_char()?; + let decoded = Self::decode_hex(byte)?; n |= decoded; } @@ -824,8 +1198,13 @@ impl<'a> Bytes<'a> { } #[inline] - fn decode_hex(&self, c: u8) -> Result { - match c { + fn decode_hex(c: char) -> Result { + if !c.is_ascii() { + return Err(Error::InvalidEscape("Non-hex digit found")); + } + + // c is an ASCII character that can be losslessly cast to u8 + match c as u8 { c @ b'0'..=b'9' => Ok(c - b'0'), c @ b'a'..=b'f' => Ok(10 + c - b'a'), c @ b'A'..=b'F' => Ok(10 + c - b'A'), @@ -833,36 +1212,76 @@ impl<'a> Bytes<'a> { } } - fn parse_escape(&mut self) -> Result { - let c = match self.eat_byte()? { - b'\'' => '\'', - b'"' => '"', - b'\\' => '\\', - b'n' => '\n', - b'r' => '\r', - b't' => '\t', - b'0' => '\0', - b'x' => self.decode_ascii_escape()? as char, - b'u' => { - self.expect_byte(b'{', Error::InvalidEscape("Missing { in Unicode escape"))?; + fn parse_escape(&mut self, encoding: EscapeEncoding, is_char: bool) -> Result { + let c = match self.next_char()? { + '\'' => EscapeCharacter::Ascii(b'\''), + '"' => EscapeCharacter::Ascii(b'"'), + '\\' => EscapeCharacter::Ascii(b'\\'), + 'n' => EscapeCharacter::Ascii(b'\n'), + 'r' => EscapeCharacter::Ascii(b'\r'), + 't' => EscapeCharacter::Ascii(b'\t'), + '0' => EscapeCharacter::Ascii(b'\0'), + 'x' => { + // Fast exit for ascii escape in byte string + let b: u8 = self.decode_ascii_escape()?; + if let EscapeEncoding::Binary = encoding { + return Ok(EscapeCharacter::Ascii(b)); + } + + // Fast exit for ascii character in UTF-8 string + let mut bytes = [b, 0, 0, 0]; + if let Ok(Some(c)) = from_utf8(&bytes[..=0]).map(|s| s.chars().next()) { + return Ok(EscapeCharacter::Utf8(c)); + } + + if is_char { + // Character literals are not allowed to use multiple byte + // escapes to build a unicode character + return Err(Error::InvalidEscape( + "Not a valid byte-escaped Unicode character", + )); + } + + // UTF-8 character needs up to four bytes and we have already + // consumed one, so at most three to go + for i in 1..4 { + if !self.consume_str(r"\x") { + return Err(Error::InvalidEscape( + "Not a valid byte-escaped Unicode character", + )); + } + + bytes[i] = self.decode_ascii_escape()?; + + // Check if we now have a valid UTF-8 character + if let Ok(Some(c)) = from_utf8(&bytes[..=i]).map(|s| s.chars().next()) { + return Ok(EscapeCharacter::Utf8(c)); + } + } + + return Err(Error::InvalidEscape( + "Not a valid byte-escaped Unicode character", + )); + } + 'u' => { + self.expect_char('{', Error::InvalidEscape("Missing { in Unicode escape"))?; let mut bytes: u32 = 0; let mut num_digits = 0; while num_digits < 6 { - let byte = self.peek_or_eof()?; + let byte = self.peek_char_or_eof()?; - if byte == b'}' { + if byte == '}' { break; - } else { - self.advance_single()?; } - let byte = self.decode_hex(byte)?; + self.skip_next_char(); + num_digits += 1; + + let byte = Self::decode_hex(byte)?; bytes <<= 4; bytes |= u32::from(byte); - - num_digits += 1; } if num_digits == 0 { @@ -871,64 +1290,74 @@ impl<'a> Bytes<'a> { )); } - self.expect_byte( - b'}', + self.expect_char( + '}', Error::InvalidEscape("No } at the end of Unicode escape"), )?; - char_from_u32(bytes).ok_or(Error::InvalidEscape("Not a valid char"))? - } - _ => { - return Err(Error::InvalidEscape("Unknown escape character")); + let c = char_from_u32(bytes).ok_or(Error::InvalidEscape( + "Not a valid Unicode-escaped character", + ))?; + + EscapeCharacter::Utf8(c) } + _ => return Err(Error::InvalidEscape("Unknown escape character")), }; Ok(c) } - fn skip_comment(&mut self) -> Result { - if self.consume("/") { - match self.eat_byte()? { - b'/' => { - let bytes = self.bytes.iter().take_while(|&&b| b != b'\n').count(); + fn skip_comment(&mut self) -> Result> { + if self.consume_char('/') { + match self.next_char()? { + '/' => { + let bytes = self.next_chars_while_len(|c| c != '\n'); - let _ = self.advance(bytes); + self.advance_bytes(bytes); + + if self.src().is_empty() { + Ok(Some(Comment::UnclosedLine)) + } else { + Ok(Some(Comment::ClosedLine)) + } } - b'*' => { + '*' => { let mut level = 1; while level > 0 { - let bytes = self - .bytes - .iter() - .take_while(|&&b| b != b'/' && b != b'*') - .count(); + let bytes = self.next_chars_while_len(|c| !matches!(c, '/' | '*')); - if self.bytes.is_empty() { + if self.src().is_empty() { return Err(Error::UnclosedBlockComment); } - let _ = self.advance(bytes); + self.advance_bytes(bytes); // check whether / or * and take action - if self.consume("/*") { + if self.consume_str("/*") { level += 1; - } else if self.consume("*/") { + } else if self.consume_str("*/") { level -= 1; } else { - self.eat_byte().map_err(|_| Error::UnclosedBlockComment)?; + self.next_char().map_err(|_| Error::UnclosedBlockComment)?; } } - } - b => return Err(Error::UnexpectedByte(b as char)), - } - Ok(true) + Ok(Some(Comment::Block)) + } + c => Err(Error::UnexpectedChar(c)), + } } else { - Ok(false) + Ok(None) } } } +enum Comment { + ClosedLine, + UnclosedLine, + Block, +} + pub trait Num { fn from_u8(x: u8) -> Self; @@ -943,7 +1372,7 @@ pub trait Num { } macro_rules! impl_num { - ($ty:ident) => { + ($ty:ty) => { impl Num for $ty { fn from_u8(x: u8) -> Self { x as $ty @@ -980,29 +1409,400 @@ macro_rules! impl_num { } } }; - ($($tys:ident)*) => { + ($($tys:ty)*) => { $( impl_num!($tys); )* }; } -#[cfg(feature = "integer128")] -impl_num!(u8 u16 u32 u64 u128 i8 i16 i32 i64 i128); -#[cfg(not(feature = "integer128"))] -impl_num!(u8 u16 u32 u64 i8 i16 i32 i64); +impl_num! { i8 i16 i32 i64 u8 u16 u32 u64 } + +#[cfg(feature = "integer128")] +impl_num! { i128 u128 } + +pub trait Integer: Sized { + fn parse(parser: &mut Parser, sign: i8) -> Result; + + fn try_from_parsed_integer(parsed: ParsedInteger, ron: &str) -> Result; +} + +macro_rules! impl_integer { + ($wrap:ident($ty:ty)) => { + impl Integer for $ty { + fn parse(parser: &mut Parser, sign: i8) -> Result { + parser.parse_integer(sign) + } + + fn try_from_parsed_integer(parsed: ParsedInteger, ron: &str) -> Result { + match parsed { + ParsedInteger::$wrap(v) => Ok(v), + _ => Err(Error::InvalidValueForType { + expected: format!( + "a{} {}-bit {}signed integer", + if <$ty>::BITS == 8 { "n" } else { "n" }, + <$ty>::BITS, + if <$ty>::MIN == 0 { "un" } else { "" }, + ), + found: String::from(ron), + }), + } + } + } + }; + ($($wraps:ident($tys:ty))*) => { + $( impl_integer!($wraps($tys)); )* + }; +} + +impl_integer! { + I8(i8) I16(i16) I32(i32) I64(i64) + U8(u8) U16(u16) U32(u32) U64(u64) +} + +#[cfg(feature = "integer128")] +impl_integer! { I128(i128) U128(u128) } + +pub enum ParsedInteger { + I8(i8), + I16(i16), + I32(i32), + I64(i64), + #[cfg(feature = "integer128")] + I128(i128), + U8(u8), + U16(u16), + U32(u32), + U64(u64), + #[cfg(feature = "integer128")] + U128(u128), +} + +impl Integer for ParsedInteger { + fn parse(parser: &mut Parser, sign: i8) -> Result { + if sign < 0 { + let signed = parser.parse_integer::(-1)?; + + return if let Ok(x) = i8::try_from(signed) { + Ok(ParsedInteger::I8(x)) + } else if let Ok(x) = i16::try_from(signed) { + Ok(ParsedInteger::I16(x)) + } else if let Ok(x) = i32::try_from(signed) { + Ok(ParsedInteger::I32(x)) + } else { + #[cfg(not(feature = "integer128"))] + { + Ok(ParsedInteger::I64(signed)) + } + #[cfg(feature = "integer128")] + if let Ok(x) = i64::try_from(signed) { + Ok(ParsedInteger::I64(x)) + } else { + Ok(ParsedInteger::I128(signed)) + } + }; + } + + let unsigned = parser.parse_integer::(1)?; + + if let Ok(x) = u8::try_from(unsigned) { + Ok(ParsedInteger::U8(x)) + } else if let Ok(x) = u16::try_from(unsigned) { + Ok(ParsedInteger::U16(x)) + } else if let Ok(x) = u32::try_from(unsigned) { + Ok(ParsedInteger::U32(x)) + } else { + #[cfg(not(feature = "integer128"))] + { + Ok(ParsedInteger::U64(unsigned)) + } + #[cfg(feature = "integer128")] + if let Ok(x) = u64::try_from(unsigned) { + Ok(ParsedInteger::U64(x)) + } else { + Ok(ParsedInteger::U128(unsigned)) + } + } + } + + fn try_from_parsed_integer(parsed: ParsedInteger, _ron: &str) -> Result { + Ok(parsed) + } +} + +pub trait Float: Sized { + fn parse(float: &str) -> Result; + + fn try_from_parsed_float(parsed: ParsedFloat, ron: &str) -> Result; +} + +macro_rules! impl_float { + ($wrap:ident($ty:ty: $bits:expr)) => { + impl Float for $ty { + fn parse(float: &str) -> Result { + <$ty>::from_str(float).map_err(|_| Error::ExpectedFloat) + } + + fn try_from_parsed_float(parsed: ParsedFloat, ron: &str) -> Result { + match parsed { + ParsedFloat::$wrap(v) => Ok(v), + _ => Err(Error::InvalidValueForType { + expected: format!( + "a {}-bit floating point number", $bits, + ), + found: String::from(ron), + }), + } + } + } + }; + ($($wraps:ident($tys:ty: $bits:expr))*) => { + $( impl_float!($wraps($tys: $bits)); )* + }; +} + +impl_float! { F32(f32: 32) F64(f64: 64) } + +pub enum ParsedFloat { + F32(f32), + F64(f64), +} + +impl Float for ParsedFloat { + fn parse(float: &str) -> Result { + let value = f64::from_str(float).map_err(|_| Error::ExpectedFloat)?; + + #[allow(clippy::cast_possible_truncation)] + if value.total_cmp(&f64::from(value as f32)).is_eq() { + Ok(ParsedFloat::F32(value as f32)) + } else { + Ok(ParsedFloat::F64(value)) + } + } + + fn try_from_parsed_float(parsed: ParsedFloat, _ron: &str) -> Result { + Ok(parsed) + } +} + +pub enum StructType { + AnyTuple, + EmptyTuple, + NewtypeTuple, + NonNewtypeTuple, + Named, + Unit, +} + +#[derive(Copy, Clone)] // GRCOV_EXCL_LINE +pub enum NewtypeMode { + NoParensMeanUnit, + InsideNewtype, +} + +#[derive(Copy, Clone)] // GRCOV_EXCL_LINE +pub enum TupleMode { + ImpreciseTupleOrNewtype, + DifferentiateNewtype, +} -#[derive(Clone, Debug)] pub enum ParsedStr<'a> { Allocated(String), Slice(&'a str), } +pub enum ParsedByteStr<'a> { + Allocated(Vec), + Slice(&'a [u8]), +} + +impl<'a> ParsedStr<'a> { + pub fn try_from_bytes(bytes: ParsedByteStr<'a>) -> Result { + match bytes { + ParsedByteStr::Allocated(byte_buf) => Ok(ParsedStr::Allocated( + String::from_utf8(byte_buf).map_err(|e| e.utf8_error())?, + )), + ParsedByteStr::Slice(bytes) => Ok(ParsedStr::Slice(from_utf8(bytes)?)), + } + } +} + +impl<'a> ParsedByteStr<'a> { + pub fn try_from_base64(str: &ParsedStr<'a>) -> Result { + let base64_str = match str { + ParsedStr::Allocated(string) => string.as_str(), + ParsedStr::Slice(str) => str, + }; + + base64::engine::Engine::decode(&base64::engine::general_purpose::STANDARD, base64_str) + .map(ParsedByteStr::Allocated) + } +} + +#[derive(Copy, Clone)] // GRCOV_EXCL_LINE +enum EscapeEncoding { + Binary, + Utf8, +} + +enum EscapeCharacter { + Ascii(u8), + Utf8(char), +} + #[cfg(test)] mod tests { use super::*; #[test] fn decode_x10() { - let mut bytes = Bytes::new(b"10").unwrap(); - assert_eq!(bytes.decode_ascii_escape(), Ok(0x10)); + let mut bytes = Parser::new("10").unwrap(); + assert_eq!(bytes.decode_ascii_escape(), Ok(b'\x10')); + } + + #[test] + fn track_prior_ws() { + const SOURCE: &str = " /*hey*/ 42 /*bye*/ 24 "; + let mut bytes = Parser::new(SOURCE).unwrap(); + + assert_eq!(bytes.src(), "42 /*bye*/ 24 "); + assert_eq!(bytes.pre_ws_src(), SOURCE); + + bytes.skip_ws().unwrap(); + + assert_eq!(bytes.src(), "42 /*bye*/ 24 "); + assert_eq!(bytes.pre_ws_src(), SOURCE); + + assert_eq!(bytes.integer::().unwrap(), 42); + + assert_eq!(bytes.src(), " /*bye*/ 24 "); + assert_eq!(bytes.pre_ws_src(), SOURCE); + + bytes.skip_ws().unwrap(); + bytes.skip_ws().unwrap(); + + assert_eq!(bytes.src(), "24 "); + assert_eq!(bytes.pre_ws_src(), " /*bye*/ 24 "); + + let mut bytes = Parser::new("42").unwrap(); + bytes.skip_ws().unwrap(); + bytes.skip_ws().unwrap(); + assert_eq!(bytes.src(), "42"); + assert_eq!(bytes.pre_ws_src(), "42"); + assert_eq!(bytes.integer::().unwrap(), 42); + bytes.skip_ws().unwrap(); + bytes.skip_ws().unwrap(); + assert_eq!(bytes.src(), ""); + assert_eq!(bytes.pre_ws_src(), ""); + + let mut bytes = Parser::new(" 42 ").unwrap(); + bytes.skip_ws().unwrap(); + bytes.skip_ws().unwrap(); + assert_eq!(bytes.src(), "42 "); + assert_eq!(bytes.pre_ws_src(), " 42 "); + assert_eq!(bytes.integer::().unwrap(), 42); + bytes.skip_ws().unwrap(); + bytes.skip_ws().unwrap(); + assert_eq!(bytes.src(), ""); + assert_eq!(bytes.pre_ws_src(), " "); + + let mut bytes = Parser::new(" 42 //").unwrap(); + bytes.skip_ws().unwrap(); + bytes.skip_ws().unwrap(); + assert_eq!(bytes.src(), "42 //"); + assert_eq!(bytes.pre_ws_src(), " 42 //"); + assert_eq!(bytes.integer::().unwrap(), 42); + bytes.skip_ws().unwrap(); + bytes.skip_ws().unwrap(); + assert_eq!(bytes.src(), ""); + assert_eq!(bytes.pre_ws_src(), " //"); + } + + #[test] + fn parser_cursor_eq_cmp() { + assert!( + ParserCursor { + cursor: 42, + pre_ws_cursor: 42, + last_ws_len: 42 + } == ParserCursor { + cursor: 42, + pre_ws_cursor: 24, + last_ws_len: 24 + } + ); + assert!( + ParserCursor { + cursor: 42, + pre_ws_cursor: 42, + last_ws_len: 42 + } != ParserCursor { + cursor: 24, + pre_ws_cursor: 42, + last_ws_len: 42 + } + ); + + assert!( + ParserCursor { + cursor: 42, + pre_ws_cursor: 42, + last_ws_len: 42 + } < ParserCursor { + cursor: 43, + pre_ws_cursor: 24, + last_ws_len: 24 + } + ); + assert!( + ParserCursor { + cursor: 42, + pre_ws_cursor: 42, + last_ws_len: 42 + } > ParserCursor { + cursor: 41, + pre_ws_cursor: 24, + last_ws_len: 24 + } + ); + } + + #[test] + fn empty_src_is_not_a_float() { + assert!(!Parser::new("").unwrap().next_bytes_is_float()); + } + + #[test] + fn v0_10_base64_deprecation_error() { + let err = crate::from_str::("\"SGVsbG8gcm9uIQ==\"").unwrap_err(); + + assert_eq!( + err, + SpannedError { + code: Error::InvalidValueForType { + expected: String::from("the Rusty byte string b\"Hello ron!\""), + found: String::from("the ambiguous base64 string \"SGVsbG8gcm9uIQ==\"") + }, + position: Position { line: 1, col: 19 }, + } + ); + + let err = crate::from_str::("r\"SGVsbG8gcm9uIQ==\"").unwrap_err(); + + assert_eq!(format!("{}", err.code), "Expected the Rusty byte string b\"Hello ron!\" but found the ambiguous base64 string \"SGVsbG8gcm9uIQ==\" instead"); + + assert_eq!( + crate::from_str::("\"invalid=\"").unwrap_err(), + SpannedError { + code: Error::ExpectedByteString, + position: Position { line: 1, col: 11 }, + } + ); + + assert_eq!( + crate::from_str::("r\"invalid=\"").unwrap_err(), + SpannedError { + code: Error::ExpectedByteString, + position: Position { line: 1, col: 12 }, + } + ); } } diff --git a/third_party/rust/ron/src/ser/mod.rs b/third_party/rust/ron/src/ser/mod.rs index b1aefbcba467..1d3b6c111c9d 100644 --- a/third_party/rust/ron/src/ser/mod.rs +++ b/third_party/rust/ron/src/ser/mod.rs @@ -1,19 +1,19 @@ -use std::io; +use std::{borrow::Cow, fmt}; -use base64::Engine; use serde::{ser, ser::Serialize}; use serde_derive::{Deserialize, Serialize}; +use unicode_ident::is_xid_continue; use crate::{ error::{Error, Result}, extensions::Extensions, options::Options, - parse::{ - is_ident_first_char, is_ident_other_char, is_ident_raw_char, LargeSInt, LargeUInt, - BASE64_ENGINE, - }, + parse::{is_ident_first_char, is_ident_raw_char, is_whitespace_char, LargeSInt, LargeUInt}, }; +pub mod path_meta; + +mod raw; #[cfg(test)] mod tests; mod value; @@ -24,7 +24,7 @@ mod value; /// if you want that, you can use [`to_writer_pretty`] instead. pub fn to_writer(writer: W, value: &T) -> Result<()> where - W: io::Write, + W: fmt::Write, T: ?Sized + Serialize, { Options::default().to_writer(writer, value) @@ -33,7 +33,7 @@ where /// Serializes `value` into `writer` in a pretty way. pub fn to_writer_pretty(writer: W, value: &T, config: PrettyConfig) -> Result<()> where - W: io::Write, + W: fmt::Write, T: ?Sized + Serialize, { Options::default().to_writer_pretty(writer, value, config) @@ -61,7 +61,6 @@ where /// Pretty serializer state struct Pretty { indent: usize, - sequence_index: Vec, } /// Pretty serializer configuration. @@ -74,37 +73,53 @@ struct Pretty { /// let my_config = PrettyConfig::new() /// .depth_limit(4) /// // definitely superior (okay, just joking) -/// .indentor("\t".to_owned()); +/// .indentor("\t"); /// ``` -#[derive(Clone, Debug, Serialize, Deserialize)] +#[allow(clippy::struct_excessive_bools)] +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] #[serde(default)] #[non_exhaustive] pub struct PrettyConfig { /// Limit the pretty-ness up to the given depth. pub depth_limit: usize, /// New line string - pub new_line: String, + pub new_line: Cow<'static, str>, /// Indentation string - pub indentor: String, + pub indentor: Cow<'static, str>, /// Separator string - pub separator: String, + pub separator: Cow<'static, str>, // Whether to emit struct names pub struct_names: bool, /// Separate tuple members with indentation pub separate_tuple_members: bool, /// Enumerate array items in comments pub enumerate_arrays: bool, - /// Enable extensions. Only configures 'implicit_some', - /// 'unwrap_newtypes', and 'unwrap_variant_newtypes' for now. + /// Enable extensions. Only configures `implicit_some`, + /// `unwrap_newtypes`, and `unwrap_variant_newtypes` for now. pub extensions: Extensions, - /// Enable compact arrays + /// Enable compact arrays, which do not insert new lines and indentation + /// between the elements of an array pub compact_arrays: bool, + /// Whether to serialize strings as escaped strings, + /// or fall back onto raw strings if necessary. + pub escape_strings: bool, + /// Enable compact structs, which do not insert new lines and indentation + /// between the fields of a struct + pub compact_structs: bool, + /// Enable compact maps, which do not insert new lines and indentation + /// between the entries of a struct + pub compact_maps: bool, + /// Enable explicit number type suffixes like `1u16` + pub number_suffixes: bool, + /// Additional path-based field metadata to serialize + pub path_meta: Option, } impl PrettyConfig { /// Creates a default [`PrettyConfig`]. + #[must_use] pub fn new() -> Self { - Default::default() + Self::default() } /// Limits the pretty-formatting based on the number of indentations. @@ -112,7 +127,8 @@ impl PrettyConfig { /// (indentation level) 6, everything will be put into the same line, /// without pretty formatting. /// - /// Default: [usize::MAX] + /// Default: [`usize::MAX`] + #[must_use] pub fn depth_limit(mut self, depth_limit: usize) -> Self { self.depth_limit = depth_limit; @@ -122,8 +138,9 @@ impl PrettyConfig { /// Configures the newlines used for serialization. /// /// Default: `\r\n` on Windows, `\n` otherwise - pub fn new_line(mut self, new_line: String) -> Self { - self.new_line = new_line; + #[must_use] + pub fn new_line(mut self, new_line: impl Into>) -> Self { + self.new_line = new_line.into(); self } @@ -131,8 +148,9 @@ impl PrettyConfig { /// Configures the string sequence used for indentation. /// /// Default: 4 spaces - pub fn indentor(mut self, indentor: String) -> Self { - self.indentor = indentor; + #[must_use] + pub fn indentor(mut self, indentor: impl Into>) -> Self { + self.indentor = indentor.into(); self } @@ -140,15 +158,19 @@ impl PrettyConfig { /// Configures the string sequence used to separate items inline. /// /// Default: 1 space - pub fn separator(mut self, separator: String) -> Self { - self.separator = separator; + #[must_use] + pub fn separator(mut self, separator: impl Into>) -> Self { + self.separator = separator.into(); self } /// Configures whether to emit struct names. /// + /// See also [`Extensions::EXPLICIT_STRUCT_NAMES`] for the extension equivalent. + /// /// Default: `false` + #[must_use] pub fn struct_names(mut self, struct_names: bool) -> Self { self.struct_names = struct_names; @@ -161,6 +183,7 @@ impl PrettyConfig { /// newlines or indentations. /// /// Default: `false` + #[must_use] pub fn separate_tuple_members(mut self, separate_tuple_members: bool) -> Self { self.separate_tuple_members = separate_tuple_members; @@ -171,6 +194,7 @@ impl PrettyConfig { /// indicating the index. /// /// Default: `false` + #[must_use] pub fn enumerate_arrays(mut self, enumerate_arrays: bool) -> Self { self.enumerate_arrays = enumerate_arrays; @@ -180,7 +204,7 @@ impl PrettyConfig { /// Configures whether every array should be a single line (`true`) /// or a multi line one (`false`). /// - /// When `false`, `["a","b"]` (as well as any array) will serialize to + /// When `false`, `["a","b"]` will serialize to /// ``` /// [ /// "a", @@ -188,13 +212,14 @@ impl PrettyConfig { /// ] /// # ; /// ``` - /// When `true`, `["a","b"]` (as well as any array) will serialize to + /// When `true`, `["a","b"]` will instead serialize to /// ``` /// ["a","b"] /// # ; /// ``` /// /// Default: `false` + #[must_use] pub fn compact_arrays(mut self, compact_arrays: bool) -> Self { self.compact_arrays = compact_arrays; @@ -203,12 +228,119 @@ impl PrettyConfig { /// Configures extensions /// - /// Default: [Extensions::empty()] + /// Default: [`Extensions::empty()`] + #[must_use] pub fn extensions(mut self, extensions: Extensions) -> Self { self.extensions = extensions; self } + + /// Configures whether strings should be serialized using escapes (true) + /// or fall back to raw strings if the string contains a `"` (false). + /// + /// When `true`, `"a\nb"` will serialize to + /// ``` + /// "a\nb" + /// # ; + /// ``` + /// When `false`, `"a\nb"` will instead serialize to + /// ``` + /// "a + /// b" + /// # ; + /// ``` + /// + /// Default: `true` + #[must_use] + pub fn escape_strings(mut self, escape_strings: bool) -> Self { + self.escape_strings = escape_strings; + + self + } + + /// Configures whether every struct should be a single line (`true`) + /// or a multi line one (`false`). + /// + /// When `false`, `Struct { a: 4, b: 2 }` will serialize to + /// ```ignore + /// Struct( + /// a: 4, + /// b: 2, + /// ) + /// # ; + /// ``` + /// When `true`, `Struct { a: 4, b: 2 }` will instead serialize to + /// ```ignore + /// Struct(a: 4, b: 2) + /// # ; + /// ``` + /// + /// Default: `false` + #[must_use] + pub fn compact_structs(mut self, compact_structs: bool) -> Self { + self.compact_structs = compact_structs; + + self + } + + /// Configures whether every map should be a single line (`true`) + /// or a multi line one (`false`). + /// + /// When `false`, a map with entries `{ "a": 4, "b": 2 }` will serialize to + /// ```ignore + /// { + /// "a": 4, + /// "b": 2, + /// } + /// # ; + /// ``` + /// When `true`, a map with entries `{ "a": 4, "b": 2 }` will instead + /// serialize to + /// ```ignore + /// {"a": 4, "b": 2} + /// # ; + /// ``` + /// + /// Default: `false` + #[must_use] + pub fn compact_maps(mut self, compact_maps: bool) -> Self { + self.compact_maps = compact_maps; + + self + } + + /// Configures whether numbers should be printed without (`false`) or + /// with (`true`) their explicit type suffixes. + /// + /// When `false`, the integer `12345u16` will serialize to + /// ```ignore + /// 12345 + /// # ; + /// ``` + /// and the float `12345.6789f64` will serialize to + /// ```ignore + /// 12345.6789 + /// # ; + /// ``` + /// When `true`, the integer `12345u16` will serialize to + /// ```ignore + /// 12345u16 + /// # ; + /// ``` + /// and the float `12345.6789f64` will serialize to + /// ```ignore + /// 12345.6789f64 + /// # ; + /// ``` + /// + /// Default: `false` + #[must_use] + pub fn number_suffixes(mut self, number_suffixes: bool) -> Self { + self.number_suffixes = number_suffixes; + + self + } } impl Default for PrettyConfig { @@ -216,17 +348,22 @@ impl Default for PrettyConfig { PrettyConfig { depth_limit: usize::MAX, new_line: if cfg!(not(target_os = "windows")) { - String::from("\n") + Cow::Borrowed("\n") } else { - String::from("\r\n") + Cow::Borrowed("\r\n") // GRCOV_EXCL_LINE }, - indentor: String::from(" "), - separator: String::from(" "), + indentor: Cow::Borrowed(" "), + separator: Cow::Borrowed(" "), struct_names: false, separate_tuple_members: false, enumerate_arrays: false, extensions: Extensions::empty(), compact_arrays: false, + escape_strings: true, + compact_structs: false, + compact_maps: false, + number_suffixes: false, + path_meta: None, } } } @@ -235,22 +372,33 @@ impl Default for PrettyConfig { /// /// You can just use [`to_string`] for deserializing a value. /// If you want it pretty-printed, take a look at [`to_string_pretty`]. -pub struct Serializer { +pub struct Serializer { output: W, pretty: Option<(PrettyConfig, Pretty)>, default_extensions: Extensions, is_empty: Option, newtype_variant: bool, recursion_limit: Option, + // Tracks the number of opened implicit `Some`s, set to 0 on backtracking + implicit_some_depth: usize, } -impl Serializer { +fn indent(output: &mut W, config: &PrettyConfig, pretty: &Pretty) -> fmt::Result { + if pretty.indent <= config.depth_limit { + for _ in 0..pretty.indent { + output.write_str(&config.indentor)?; + } + } + Ok(()) +} + +impl Serializer { /// Creates a new [`Serializer`]. /// /// Most of the time you can just use [`to_string`] or /// [`to_string_pretty`]. pub fn new(writer: W, config: Option) -> Result { - Self::with_options(writer, config, Options::default()) + Self::with_options(writer, config, &Options::default()) } /// Creates a new [`Serializer`]. @@ -260,54 +408,71 @@ impl Serializer { pub fn with_options( mut writer: W, config: Option, - options: Options, + options: &Options, ) -> Result { if let Some(conf) = &config { + if !conf.new_line.chars().all(is_whitespace_char) { + return Err(Error::Message(String::from( + "Invalid non-whitespace `PrettyConfig::new_line`", + ))); + } + if !conf.indentor.chars().all(is_whitespace_char) { + return Err(Error::Message(String::from( + "Invalid non-whitespace `PrettyConfig::indentor`", + ))); + } + if !conf.separator.chars().all(is_whitespace_char) { + return Err(Error::Message(String::from( + "Invalid non-whitespace `PrettyConfig::separator`", + ))); + } + let non_default_extensions = !options.default_extensions; - if (non_default_extensions & conf.extensions).contains(Extensions::IMPLICIT_SOME) { - writer.write_all(b"#![enable(implicit_some)]")?; - writer.write_all(conf.new_line.as_bytes())?; - }; - if (non_default_extensions & conf.extensions).contains(Extensions::UNWRAP_NEWTYPES) { - writer.write_all(b"#![enable(unwrap_newtypes)]")?; - writer.write_all(conf.new_line.as_bytes())?; - }; - if (non_default_extensions & conf.extensions) - .contains(Extensions::UNWRAP_VARIANT_NEWTYPES) - { - writer.write_all(b"#![enable(unwrap_variant_newtypes)]")?; - writer.write_all(conf.new_line.as_bytes())?; - }; + for (extension_name, _) in (non_default_extensions & conf.extensions).iter_names() { + write!(writer, "#![enable({})]", extension_name.to_lowercase())?; + writer.write_str(&conf.new_line)?; + } }; Ok(Serializer { output: writer, - pretty: config.map(|conf| { - ( - conf, - Pretty { - indent: 0, - sequence_index: Vec::new(), - }, - ) - }), + pretty: config.map(|conf| (conf, Pretty { indent: 0 })), default_extensions: options.default_extensions, is_empty: None, newtype_variant: false, recursion_limit: options.recursion_limit, + implicit_some_depth: 0, }) } fn separate_tuple_members(&self) -> bool { self.pretty .as_ref() - .map_or(false, |&(ref config, _)| config.separate_tuple_members) + .map_or(false, |(ref config, _)| config.separate_tuple_members) } fn compact_arrays(&self) -> bool { self.pretty .as_ref() - .map_or(false, |&(ref config, _)| config.compact_arrays) + .map_or(false, |(ref config, _)| config.compact_arrays) + } + + fn compact_structs(&self) -> bool { + self.pretty + .as_ref() + .map_or(false, |(ref config, _)| config.compact_structs) + } + + fn compact_maps(&self) -> bool { + self.pretty + .as_ref() + .map_or(false, |(ref config, _)| config.compact_maps) + } + + fn number_suffixes(&self) -> bool { + self.pretty + .as_ref() + .map_or(false, |(ref config, _)| config.number_suffixes) } fn extensions(&self) -> Extensions { @@ -315,7 +480,13 @@ impl Serializer { | self .pretty .as_ref() - .map_or(Extensions::empty(), |&(ref config, _)| config.extensions) + .map_or(Extensions::empty(), |(ref config, _)| config.extensions) + } + + fn escape_strings(&self) -> bool { + self.pretty + .as_ref() + .map_or(true, |(ref config, _)| config.escape_strings) } fn start_indent(&mut self) -> Result<()> { @@ -325,32 +496,28 @@ impl Serializer { let is_empty = self.is_empty.unwrap_or(false); if !is_empty { - self.output.write_all(config.new_line.as_bytes())?; + self.output.write_str(&config.new_line)?; } } } Ok(()) } - fn indent(&mut self) -> io::Result<()> { + fn indent(&mut self) -> fmt::Result { if let Some((ref config, ref pretty)) = self.pretty { - if pretty.indent <= config.depth_limit { - for _ in 0..pretty.indent { - self.output.write_all(config.indentor.as_bytes())?; - } - } + indent(&mut self.output, config, pretty)?; } Ok(()) } - fn end_indent(&mut self) -> io::Result<()> { + fn end_indent(&mut self) -> fmt::Result { if let Some((ref config, ref mut pretty)) = self.pretty { if pretty.indent <= config.depth_limit { let is_empty = self.is_empty.unwrap_or(false); if !is_empty { for _ in 1..pretty.indent { - self.output.write_all(config.indentor.as_bytes())?; + self.output.write_str(&config.indentor)?; } } } @@ -361,48 +528,126 @@ impl Serializer { Ok(()) } - fn serialize_escaped_str(&mut self, value: &str) -> io::Result<()> { - self.output.write_all(b"\"")?; + fn serialize_escaped_str(&mut self, value: &str) -> fmt::Result { + self.output.write_char('"')?; let mut scalar = [0u8; 4]; - for c in value.chars().flat_map(|c| c.escape_debug()) { - self.output - .write_all(c.encode_utf8(&mut scalar).as_bytes())?; + for c in value.chars().flat_map(char::escape_debug) { + self.output.write_str(c.encode_utf8(&mut scalar))?; } - self.output.write_all(b"\"")?; + self.output.write_char('"')?; Ok(()) } - fn serialize_sint(&mut self, value: impl Into) -> Result<()> { + fn serialize_unescaped_or_raw_str(&mut self, value: &str) -> fmt::Result { + if value.contains('"') || value.contains('\\') { + let (_, num_consecutive_hashes) = + value.chars().fold((0, 0), |(count, max), c| match c { + '#' => (count + 1, max.max(count + 1)), + _ => (0_usize, max), + }); + let hashes: String = "#".repeat(num_consecutive_hashes + 1); + self.output.write_char('r')?; + self.output.write_str(&hashes)?; + self.output.write_char('"')?; + self.output.write_str(value)?; + self.output.write_char('"')?; + self.output.write_str(&hashes)?; + } else { + self.output.write_char('"')?; + self.output.write_str(value)?; + self.output.write_char('"')?; + } + Ok(()) + } + + fn serialize_escaped_byte_str(&mut self, value: &[u8]) -> fmt::Result { + self.output.write_str("b\"")?; + for c in value.iter().flat_map(|c| std::ascii::escape_default(*c)) { + self.output.write_char(char::from(c))?; + } + self.output.write_char('"')?; + Ok(()) + } + + fn serialize_unescaped_or_raw_byte_str(&mut self, value: &str) -> fmt::Result { + if value.contains('"') || value.contains('\\') { + let (_, num_consecutive_hashes) = + value.chars().fold((0, 0), |(count, max), c| match c { + '#' => (count + 1, max.max(count + 1)), + _ => (0_usize, max), + }); + let hashes: String = "#".repeat(num_consecutive_hashes + 1); + self.output.write_str("br")?; + self.output.write_str(&hashes)?; + self.output.write_char('"')?; + self.output.write_str(value)?; + self.output.write_char('"')?; + self.output.write_str(&hashes)?; + } else { + self.output.write_str("b\"")?; + self.output.write_str(value)?; + self.output.write_char('"')?; + } + Ok(()) + } + + fn serialize_sint(&mut self, value: impl Into, suffix: &str) -> Result<()> { // TODO optimize write!(self.output, "{}", value.into())?; + if self.number_suffixes() { + write!(self.output, "{}", suffix)?; + } + Ok(()) } - fn serialize_uint(&mut self, value: impl Into) -> Result<()> { + fn serialize_uint(&mut self, value: impl Into, suffix: &str) -> Result<()> { // TODO optimize write!(self.output, "{}", value.into())?; + if self.number_suffixes() { + write!(self.output, "{}", suffix)?; + } + Ok(()) } fn write_identifier(&mut self, name: &str) -> Result<()> { - if name.is_empty() || !name.as_bytes().iter().copied().all(is_ident_raw_char) { - return Err(Error::InvalidIdentifier(name.into())); + self.validate_identifier(name)?; + let mut chars = name.chars(); + if !chars.next().map_or(false, is_ident_first_char) + || !chars.all(is_xid_continue) + || [ + "true", "false", "Some", "None", "inf", "inff32", "inff64", "NaN", "NaNf32", + "NaNf64", + ] + .contains(&name) + { + self.output.write_str("r#")?; } - let mut bytes = name.as_bytes().iter().copied(); - if !bytes.next().map_or(false, is_ident_first_char) || !bytes.all(is_ident_other_char) { - self.output.write_all(b"r#")?; - } - self.output.write_all(name.as_bytes())?; + self.output.write_str(name)?; Ok(()) } + #[allow(clippy::unused_self)] + fn validate_identifier(&self, name: &str) -> Result<()> { + if name.is_empty() || !name.chars().all(is_ident_raw_char) { + return Err(Error::InvalidIdentifier(name.into())); + } + Ok(()) + } + + /// Checks if struct names should be emitted + /// + /// Note that when using the `explicit_struct_names` extension, this method will use an OR operation on the extension and the [`PrettyConfig::struct_names`] option. See also [`Extensions::EXPLICIT_STRUCT_NAMES`] for the extension equivalent. fn struct_names(&self) -> bool { - self.pretty - .as_ref() - .map(|(pc, _)| pc.struct_names) - .unwrap_or(false) + self.extensions() + .contains(Extensions::EXPLICIT_STRUCT_NAMES) + || self + .pretty + .as_ref() + .map_or(false, |(pc, _)| pc.struct_names) } } @@ -426,7 +671,7 @@ macro_rules! guard_recursion { }}; } -impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { +impl<'a, W: fmt::Write> ser::Serializer for &'a mut Serializer { type Error = Error; type Ok = (); type SerializeMap = Compound<'a, W>; @@ -438,90 +683,135 @@ impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { type SerializeTupleVariant = Compound<'a, W>; fn serialize_bool(self, v: bool) -> Result<()> { - self.output.write_all(if v { b"true" } else { b"false" })?; + self.output.write_str(if v { "true" } else { "false" })?; Ok(()) } fn serialize_i8(self, v: i8) -> Result<()> { - self.serialize_sint(v) + self.serialize_sint(v, "i8") } fn serialize_i16(self, v: i16) -> Result<()> { - self.serialize_sint(v) + self.serialize_sint(v, "i16") } fn serialize_i32(self, v: i32) -> Result<()> { - self.serialize_sint(v) + self.serialize_sint(v, "i32") } fn serialize_i64(self, v: i64) -> Result<()> { - self.serialize_sint(v) + self.serialize_sint(v, "i64") } #[cfg(feature = "integer128")] fn serialize_i128(self, v: i128) -> Result<()> { - self.serialize_sint(v) + self.serialize_sint(v, "i128") } fn serialize_u8(self, v: u8) -> Result<()> { - self.serialize_uint(v) + self.serialize_uint(v, "u8") } fn serialize_u16(self, v: u16) -> Result<()> { - self.serialize_uint(v) + self.serialize_uint(v, "u16") } fn serialize_u32(self, v: u32) -> Result<()> { - self.serialize_uint(v) + self.serialize_uint(v, "u32") } fn serialize_u64(self, v: u64) -> Result<()> { - self.serialize_uint(v) + self.serialize_uint(v, "u64") } #[cfg(feature = "integer128")] fn serialize_u128(self, v: u128) -> Result<()> { - self.serialize_uint(v) + self.serialize_uint(v, "u128") } fn serialize_f32(self, v: f32) -> Result<()> { + if v.is_nan() && v.is_sign_negative() { + write!(self.output, "-")?; + } + write!(self.output, "{}", v)?; + if v.fract() == 0.0 { write!(self.output, ".0")?; } + + if self.number_suffixes() { + write!(self.output, "f32")?; + } + Ok(()) } fn serialize_f64(self, v: f64) -> Result<()> { + if v.is_nan() && v.is_sign_negative() { + write!(self.output, "-")?; + } + write!(self.output, "{}", v)?; + if v.fract() == 0.0 { write!(self.output, ".0")?; } + + if self.number_suffixes() { + write!(self.output, "f64")?; + } + Ok(()) } fn serialize_char(self, v: char) -> Result<()> { - self.output.write_all(b"'")?; + self.output.write_char('\'')?; if v == '\\' || v == '\'' { - self.output.write_all(b"\\")?; + self.output.write_char('\\')?; } write!(self.output, "{}", v)?; - self.output.write_all(b"'")?; + self.output.write_char('\'')?; Ok(()) } fn serialize_str(self, v: &str) -> Result<()> { - self.serialize_escaped_str(v)?; + if self.escape_strings() { + self.serialize_escaped_str(v)?; + } else { + self.serialize_unescaped_or_raw_str(v)?; + } Ok(()) } fn serialize_bytes(self, v: &[u8]) -> Result<()> { - self.serialize_str(BASE64_ENGINE.encode(v).as_str()) + // We need to fall back to escaping if the byte string would be invalid UTF-8 + if !self.escape_strings() { + if let Ok(v) = std::str::from_utf8(v) { + return self + .serialize_unescaped_or_raw_byte_str(v) + .map_err(Error::from); + } + } + + self.serialize_escaped_byte_str(v)?; + + Ok(()) } fn serialize_none(self) -> Result<()> { - self.output.write_all(b"None")?; + // We no longer need to keep track of the depth + let implicit_some_depth = self.implicit_some_depth; + self.implicit_some_depth = 0; + + for _ in 0..implicit_some_depth { + self.output.write_str("Some(")?; + } + self.output.write_str("None")?; + for _ in 0..implicit_some_depth { + self.output.write_char(')')?; + } Ok(()) } @@ -531,12 +821,20 @@ impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { T: ?Sized + Serialize, { let implicit_some = self.extensions().contains(Extensions::IMPLICIT_SOME); - if !implicit_some { - self.output.write_all(b"Some(")?; + if implicit_some { + self.implicit_some_depth += 1; + } else { + self.newtype_variant = self + .extensions() + .contains(Extensions::UNWRAP_VARIANT_NEWTYPES); + self.output.write_str("Some(")?; } guard_recursion! { self => value.serialize(&mut *self)? }; - if !implicit_some { - self.output.write_all(b")")?; + if implicit_some { + self.implicit_some_depth = 0; + } else { + self.output.write_char(')')?; + self.newtype_variant = false; } Ok(()) @@ -544,11 +842,9 @@ impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { fn serialize_unit(self) -> Result<()> { if !self.newtype_variant { - self.output.write_all(b"()")?; + self.output.write_str("()")?; } - self.newtype_variant = false; - Ok(()) } @@ -558,11 +854,18 @@ impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { Ok(()) } else { + self.validate_identifier(name)?; self.serialize_unit() } } - fn serialize_unit_variant(self, _: &'static str, _: u32, variant: &'static str) -> Result<()> { + fn serialize_unit_variant( + self, + name: &'static str, + _variant_index: u32, + variant: &'static str, + ) -> Result<()> { + self.validate_identifier(name)?; self.write_identifier(variant)?; Ok(()) @@ -572,73 +875,97 @@ impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { where T: ?Sized + Serialize, { + if name == crate::value::raw::RAW_VALUE_TOKEN { + let implicit_some_depth = self.implicit_some_depth; + self.implicit_some_depth = 0; + + for _ in 0..implicit_some_depth { + self.output.write_str("Some(")?; + } + + guard_recursion! { self => value.serialize(raw::Serializer::new(self)) }?; + + for _ in 0..implicit_some_depth { + self.output.write_char(')')?; + } + + return Ok(()); + } + if self.extensions().contains(Extensions::UNWRAP_NEWTYPES) || self.newtype_variant { self.newtype_variant = false; + self.validate_identifier(name)?; + return guard_recursion! { self => value.serialize(&mut *self) }; } if self.struct_names() { self.write_identifier(name)?; + } else { + self.validate_identifier(name)?; } - self.output.write_all(b"(")?; + self.implicit_some_depth = 0; + + self.output.write_char('(')?; guard_recursion! { self => value.serialize(&mut *self)? }; - self.output.write_all(b")")?; + self.output.write_char(')')?; + Ok(()) } fn serialize_newtype_variant( self, - _: &'static str, - _: u32, + name: &'static str, + _variant_index: u32, variant: &'static str, value: &T, ) -> Result<()> where T: ?Sized + Serialize, { + self.validate_identifier(name)?; self.write_identifier(variant)?; - self.output.write_all(b"(")?; + self.output.write_char('(')?; self.newtype_variant = self .extensions() .contains(Extensions::UNWRAP_VARIANT_NEWTYPES); + self.implicit_some_depth = 0; guard_recursion! { self => value.serialize(&mut *self)? }; self.newtype_variant = false; - self.output.write_all(b")")?; + self.output.write_char(')')?; Ok(()) } fn serialize_seq(self, len: Option) -> Result { self.newtype_variant = false; + self.implicit_some_depth = 0; - self.output.write_all(b"[")?; - - if let Some(len) = len { - self.is_empty = Some(len == 0); - } + self.output.write_char('[')?; if !self.compact_arrays() { + if let Some(len) = len { + self.is_empty = Some(len == 0); + } + self.start_indent()?; } - if let Some((_, ref mut pretty)) = self.pretty { - pretty.sequence_index.push(0); - } - - Compound::try_new(self, false) + Ok(Compound::new(self, false)) } fn serialize_tuple(self, len: usize) -> Result { let old_newtype_variant = self.newtype_variant; self.newtype_variant = false; + self.implicit_some_depth = 0; if !old_newtype_variant { - self.output.write_all(b"(")?; + self.output.write_char('(')?; } if self.separate_tuple_members() { @@ -647,7 +974,7 @@ impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { self.start_indent()?; } - Compound::try_new(self, old_newtype_variant) + Ok(Compound::new(self, old_newtype_variant)) } fn serialize_tuple_struct( @@ -657,6 +984,8 @@ impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { ) -> Result { if self.struct_names() && !self.newtype_variant { self.write_identifier(name)?; + } else { + self.validate_identifier(name)?; } self.serialize_tuple(len) @@ -664,15 +993,17 @@ impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { fn serialize_tuple_variant( self, - _: &'static str, - _: u32, + name: &'static str, + _variant_index: u32, variant: &'static str, len: usize, ) -> Result { self.newtype_variant = false; + self.implicit_some_depth = 0; + self.validate_identifier(name)?; self.write_identifier(variant)?; - self.output.write_all(b"(")?; + self.output.write_char('(')?; if self.separate_tuple_members() { self.is_empty = Some(len == 0); @@ -680,56 +1011,70 @@ impl<'a, W: io::Write> ser::Serializer for &'a mut Serializer { self.start_indent()?; } - Compound::try_new(self, false) + Ok(Compound::new(self, false)) } fn serialize_map(self, len: Option) -> Result { self.newtype_variant = false; + self.implicit_some_depth = 0; - self.output.write_all(b"{")?; + self.output.write_char('{')?; - if let Some(len) = len { - self.is_empty = Some(len == 0); + if !self.compact_maps() { + if let Some(len) = len { + self.is_empty = Some(len == 0); + } + + self.start_indent()?; } - self.start_indent()?; - - Compound::try_new(self, false) + Ok(Compound::new(self, false)) } fn serialize_struct(self, name: &'static str, len: usize) -> Result { let old_newtype_variant = self.newtype_variant; self.newtype_variant = false; + self.implicit_some_depth = 0; - if !old_newtype_variant { + if old_newtype_variant { + self.validate_identifier(name)?; + } else { if self.struct_names() { self.write_identifier(name)?; + } else { + self.validate_identifier(name)?; } - self.output.write_all(b"(")?; + self.output.write_char('(')?; } - self.is_empty = Some(len == 0); - self.start_indent()?; + if !self.compact_structs() { + self.is_empty = Some(len == 0); + self.start_indent()?; + } - Compound::try_new(self, old_newtype_variant) + Ok(Compound::new(self, old_newtype_variant)) } fn serialize_struct_variant( self, - _: &'static str, - _: u32, + name: &'static str, + _variant_index: u32, variant: &'static str, len: usize, ) -> Result { self.newtype_variant = false; + self.implicit_some_depth = 0; + self.validate_identifier(name)?; self.write_identifier(variant)?; - self.output.write_all(b"(")?; + self.output.write_char('(')?; - self.is_empty = Some(len == 0); - self.start_indent()?; + if !self.compact_structs() { + self.is_empty = Some(len == 0); + self.start_indent()?; + } - Compound::try_new(self, false) + Ok(Compound::new(self, false)) } } @@ -739,31 +1084,25 @@ enum State { } #[doc(hidden)] -pub struct Compound<'a, W: io::Write> { +pub struct Compound<'a, W: fmt::Write> { ser: &'a mut Serializer, state: State, newtype_variant: bool, + sequence_index: usize, } -impl<'a, W: io::Write> Compound<'a, W> { - fn try_new(ser: &'a mut Serializer, newtype_variant: bool) -> Result { - if let Some(limit) = &mut ser.recursion_limit { - if let Some(new_limit) = limit.checked_sub(1) { - *limit = new_limit; - } else { - return Err(Error::ExceededRecursionLimit); - } - } - - Ok(Compound { +impl<'a, W: fmt::Write> Compound<'a, W> { + fn new(ser: &'a mut Serializer, newtype_variant: bool) -> Self { + Compound { ser, state: State::First, newtype_variant, - }) + sequence_index: 0, + } } } -impl<'a, W: io::Write> Drop for Compound<'a, W> { +impl<'a, W: fmt::Write> Drop for Compound<'a, W> { fn drop(&mut self) { if let Some(limit) = &mut self.ser.recursion_limit { *limit = limit.saturating_add(1); @@ -771,7 +1110,7 @@ impl<'a, W: io::Write> Drop for Compound<'a, W> { } } -impl<'a, W: io::Write> ser::SerializeSeq for Compound<'a, W> { +impl<'a, W: fmt::Write> ser::SerializeSeq for Compound<'a, W> { type Error = Error; type Ok = (); @@ -782,12 +1121,12 @@ impl<'a, W: io::Write> ser::SerializeSeq for Compound<'a, W> { if let State::First = self.state { self.state = State::Rest; } else { - self.ser.output.write_all(b",")?; + self.ser.output.write_char(',')?; if let Some((ref config, ref mut pretty)) = self.ser.pretty { if pretty.indent <= config.depth_limit && !config.compact_arrays { - self.ser.output.write_all(config.new_line.as_bytes())?; + self.ser.output.write_str(&config.new_line)?; } else { - self.ser.output.write_all(config.separator.as_bytes())?; + self.ser.output.write_str(&config.separator)?; } } } @@ -798,9 +1137,8 @@ impl<'a, W: io::Write> ser::SerializeSeq for Compound<'a, W> { if let Some((ref mut config, ref mut pretty)) = self.ser.pretty { if pretty.indent <= config.depth_limit && config.enumerate_arrays { - let index = pretty.sequence_index.last_mut().unwrap(); - write!(self.ser.output, "/*[{}]*/ ", index)?; - *index += 1; + write!(self.ser.output, "/*[{}]*/ ", self.sequence_index)?; + self.sequence_index += 1; } } @@ -813,8 +1151,8 @@ impl<'a, W: io::Write> ser::SerializeSeq for Compound<'a, W> { if let State::Rest = self.state { if let Some((ref config, ref mut pretty)) = self.ser.pretty { if pretty.indent <= config.depth_limit && !config.compact_arrays { - self.ser.output.write_all(b",")?; - self.ser.output.write_all(config.new_line.as_bytes())?; + self.ser.output.write_char(',')?; + self.ser.output.write_str(&config.new_line)?; } } } @@ -823,17 +1161,13 @@ impl<'a, W: io::Write> ser::SerializeSeq for Compound<'a, W> { self.ser.end_indent()?; } - if let Some((_, ref mut pretty)) = self.ser.pretty { - pretty.sequence_index.pop(); - } - // seq always disables `self.newtype_variant` - self.ser.output.write_all(b"]")?; + self.ser.output.write_char(']')?; Ok(()) } } -impl<'a, W: io::Write> ser::SerializeTuple for Compound<'a, W> { +impl<'a, W: fmt::Write> ser::SerializeTuple for Compound<'a, W> { type Error = Error; type Ok = (); @@ -844,12 +1178,12 @@ impl<'a, W: io::Write> ser::SerializeTuple for Compound<'a, W> { if let State::First = self.state { self.state = State::Rest; } else { - self.ser.output.write_all(b",")?; + self.ser.output.write_char(',')?; if let Some((ref config, ref pretty)) = self.ser.pretty { if pretty.indent <= config.depth_limit && self.ser.separate_tuple_members() { - self.ser.output.write_all(config.new_line.as_bytes())?; + self.ser.output.write_str(&config.new_line)?; } else { - self.ser.output.write_all(config.separator.as_bytes())?; + self.ser.output.write_str(&config.separator)?; } } } @@ -867,8 +1201,8 @@ impl<'a, W: io::Write> ser::SerializeTuple for Compound<'a, W> { if let State::Rest = self.state { if let Some((ref config, ref pretty)) = self.ser.pretty { if self.ser.separate_tuple_members() && pretty.indent <= config.depth_limit { - self.ser.output.write_all(b",")?; - self.ser.output.write_all(config.new_line.as_bytes())?; + self.ser.output.write_char(',')?; + self.ser.output.write_str(&config.new_line)?; } } } @@ -877,7 +1211,7 @@ impl<'a, W: io::Write> ser::SerializeTuple for Compound<'a, W> { } if !self.newtype_variant { - self.ser.output.write_all(b")")?; + self.ser.output.write_char(')')?; } Ok(()) @@ -885,7 +1219,7 @@ impl<'a, W: io::Write> ser::SerializeTuple for Compound<'a, W> { } // Same thing but for tuple structs. -impl<'a, W: io::Write> ser::SerializeTupleStruct for Compound<'a, W> { +impl<'a, W: fmt::Write> ser::SerializeTupleStruct for Compound<'a, W> { type Error = Error; type Ok = (); @@ -901,7 +1235,7 @@ impl<'a, W: io::Write> ser::SerializeTupleStruct for Compound<'a, W> { } } -impl<'a, W: io::Write> ser::SerializeTupleVariant for Compound<'a, W> { +impl<'a, W: fmt::Write> ser::SerializeTupleVariant for Compound<'a, W> { type Error = Error; type Ok = (); @@ -917,7 +1251,7 @@ impl<'a, W: io::Write> ser::SerializeTupleVariant for Compound<'a, W> { } } -impl<'a, W: io::Write> ser::SerializeMap for Compound<'a, W> { +impl<'a, W: fmt::Write> ser::SerializeMap for Compound<'a, W> { type Error = Error; type Ok = (); @@ -928,17 +1262,21 @@ impl<'a, W: io::Write> ser::SerializeMap for Compound<'a, W> { if let State::First = self.state { self.state = State::Rest; } else { - self.ser.output.write_all(b",")?; + self.ser.output.write_char(',')?; if let Some((ref config, ref pretty)) = self.ser.pretty { - if pretty.indent <= config.depth_limit { - self.ser.output.write_all(config.new_line.as_bytes())?; + if pretty.indent <= config.depth_limit && !config.compact_maps { + self.ser.output.write_str(&config.new_line)?; } else { - self.ser.output.write_all(config.separator.as_bytes())?; + self.ser.output.write_str(&config.separator)?; } } } - self.ser.indent()?; + + if !self.ser.compact_maps() { + self.ser.indent()?; + } + guard_recursion! { self.ser => key.serialize(&mut *self.ser) } } @@ -946,10 +1284,10 @@ impl<'a, W: io::Write> ser::SerializeMap for Compound<'a, W> { where T: ?Sized + Serialize, { - self.ser.output.write_all(b":")?; + self.ser.output.write_char(':')?; if let Some((ref config, _)) = self.ser.pretty { - self.ser.output.write_all(config.separator.as_bytes())?; + self.ser.output.write_str(&config.separator)?; } guard_recursion! { self.ser => value.serialize(&mut *self.ser)? }; @@ -960,20 +1298,24 @@ impl<'a, W: io::Write> ser::SerializeMap for Compound<'a, W> { fn end(self) -> Result<()> { if let State::Rest = self.state { if let Some((ref config, ref pretty)) = self.ser.pretty { - if pretty.indent <= config.depth_limit { - self.ser.output.write_all(b",")?; - self.ser.output.write_all(config.new_line.as_bytes())?; + if pretty.indent <= config.depth_limit && !config.compact_maps { + self.ser.output.write_char(',')?; + self.ser.output.write_str(&config.new_line)?; } } } - self.ser.end_indent()?; + + if !self.ser.compact_maps() { + self.ser.end_indent()?; + } + // map always disables `self.newtype_variant` - self.ser.output.write_all(b"}")?; + self.ser.output.write_char('}')?; Ok(()) } } -impl<'a, W: io::Write> ser::SerializeStruct for Compound<'a, W> { +impl<'a, W: fmt::Write> ser::SerializeStruct for Compound<'a, W> { type Error = Error; type Ok = (); @@ -981,50 +1323,91 @@ impl<'a, W: io::Write> ser::SerializeStruct for Compound<'a, W> { where T: ?Sized + Serialize, { + let mut restore_field = self.ser.pretty.as_mut().and_then(|(config, _)| { + config.path_meta.take().map(|mut field| { + if let Some(fields) = field.fields_mut() { + config.path_meta = fields.remove(key); + } + field + }) + }); + if let State::First = self.state { self.state = State::Rest; } else { - self.ser.output.write_all(b",")?; + self.ser.output.write_char(',')?; if let Some((ref config, ref pretty)) = self.ser.pretty { - if pretty.indent <= config.depth_limit { - self.ser.output.write_all(config.new_line.as_bytes())?; + if pretty.indent <= config.depth_limit && !config.compact_structs { + self.ser.output.write_str(&config.new_line)?; } else { - self.ser.output.write_all(config.separator.as_bytes())?; + self.ser.output.write_str(&config.separator)?; } } } - self.ser.indent()?; + + if !self.ser.compact_structs() { + if let Some((ref config, ref pretty)) = self.ser.pretty { + indent(&mut self.ser.output, config, pretty)?; + + if let Some(ref field) = config.path_meta { + for doc_line in field.doc().lines() { + self.ser.output.write_str("/// ")?; + self.ser.output.write_str(doc_line)?; + self.ser.output.write_char('\n')?; + indent(&mut self.ser.output, config, pretty)?; + } + } + } + } + self.ser.write_identifier(key)?; - self.ser.output.write_all(b":")?; + self.ser.output.write_char(':')?; if let Some((ref config, _)) = self.ser.pretty { - self.ser.output.write_all(config.separator.as_bytes())?; + self.ser.output.write_str(&config.separator)?; } guard_recursion! { self.ser => value.serialize(&mut *self.ser)? }; + if let Some((ref mut config, _)) = self.ser.pretty { + std::mem::swap(&mut config.path_meta, &mut restore_field); + + if let Some(ref mut field) = config.path_meta { + if let Some(fields) = field.fields_mut() { + if let Some(restore_field) = restore_field { + fields.insert(key, restore_field); + } + } + } + }; + Ok(()) } fn end(self) -> Result<()> { if let State::Rest = self.state { if let Some((ref config, ref pretty)) = self.ser.pretty { - if pretty.indent <= config.depth_limit { - self.ser.output.write_all(b",")?; - self.ser.output.write_all(config.new_line.as_bytes())?; + if pretty.indent <= config.depth_limit && !config.compact_structs { + self.ser.output.write_char(',')?; + self.ser.output.write_str(&config.new_line)?; } } } - self.ser.end_indent()?; - if !self.newtype_variant { - self.ser.output.write_all(b")")?; + + if !self.ser.compact_structs() { + self.ser.end_indent()?; } + + if !self.newtype_variant { + self.ser.output.write_char(')')?; + } + Ok(()) } } -impl<'a, W: io::Write> ser::SerializeStructVariant for Compound<'a, W> { +impl<'a, W: fmt::Write> ser::SerializeStructVariant for Compound<'a, W> { type Error = Error; type Ok = (); diff --git a/third_party/rust/ron/src/ser/path_meta.rs b/third_party/rust/ron/src/ser/path_meta.rs new file mode 100644 index 000000000000..8a870523ef44 --- /dev/null +++ b/third_party/rust/ron/src/ser/path_meta.rs @@ -0,0 +1,360 @@ +//! Path-based metadata to serialize with a value. +//! +//! Path-based in this context means that the metadata is linked +//! to the data in a relative and hierarchical fashion by tracking +//! the current absolute path of the field being serialized. +//! +//! # Example +//! +//! ``` +//! # use ron::ser::{PrettyConfig, path_meta::Field}; +//! +//! #[derive(serde::Serialize)] +//! struct Creature { +//! seconds_since_existing: usize, +//! linked: Option>, +//! } +//! +//! let mut config = PrettyConfig::default(); +//! +//! config +//! .path_meta +//! // The path meta defaults to no root structure, +//! // so we either provide a prebuilt one or initialize +//! // an empty one to build. +//! .get_or_insert_with(Field::empty) +//! .build_fields(|fields| { +//! fields +//! // Get or insert the named field +//! .field("seconds_since_existing") +//! .with_doc("Outer seconds_since_existing"); +//! fields +//! .field("linked") +//! // Doc metadata is serialized preceded by three forward slashes and a space for each line +//! .with_doc("Optional.\nProvide another creature to be wrapped.") +//! // Even though it's another Creature, the fields have different paths, so they are addressed separately. +//! .build_fields(|fields| { +//! fields +//! .field("seconds_since_existing") +//! .with_doc("Inner seconds_since_existing"); +//! }); +//! }); +//! +//! let value = Creature { +//! seconds_since_existing: 0, +//! linked: Some(Box::new(Creature { +//! seconds_since_existing: 0, +//! linked: None, +//! })), +//! }; +//! +//! let s = ron::ser::to_string_pretty(&value, config).unwrap(); +//! +//! assert_eq!(s, r#"( +//! /// Outer seconds_since_existing +//! seconds_since_existing: 0, +//! /// Optional. +//! /// Provide another creature to be wrapped. +//! linked: Some(( +//! /// Inner seconds_since_existing +//! seconds_since_existing: 0, +//! linked: None, +//! )), +//! )"#); +//! ``` +//! +//! # Identical paths +//! +//! Especially in enums and tuples it's possible for fields +//! to share a path, thus being unable to be addressed separately. +//! +//! ```no_run +//! enum Kind { +//! A { +//! field: (), +//! }, // ^ +//! // cannot be addressed separately because they have the same path +//! B { // v +//! field: (), +//! }, +//! } +//! ``` +//! +//! ```no_run +//! struct A { +//! field: (), +//! } +//! +//! struct B { +//! field: (), +//! } +//! +//! type Value = ( +//! A, +//! // ^ +//! // These are different types, but they share the path `field` +//! // v +//! B, +//! ); +//! ``` + +use std::collections::HashMap; + +use serde_derive::{Deserialize, Serialize}; + +/// The metadata and inner [`Fields`] of a field. +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] +pub struct Field { + doc: String, + fields: Option, +} + +impl Field { + /// Create a new empty field metadata. + #[must_use] + pub const fn empty() -> Self { + Self { + doc: String::new(), + fields: None, + } + } + + /// Create a new field metadata from parts. + pub fn new(doc: impl Into, fields: Option) -> Self { + Self { + doc: doc.into(), + fields, + } + } + + /// Get a shared reference to the documentation metadata of this field. + #[inline] + #[must_use] + pub fn doc(&self) -> &str { + self.doc.as_str() + } + + /// Get a mutable reference to the documentation metadata of this field. + #[inline] + #[must_use] + pub fn doc_mut(&mut self) -> &mut String { + &mut self.doc + } + + /// Set the documentation metadata of this field. + /// + /// ``` + /// # use ron::ser::path_meta::Field; + /// + /// let mut field = Field::empty(); + /// + /// assert_eq!(field.doc(), ""); + /// + /// field.with_doc("some meta"); + /// + /// assert_eq!(field.doc(), "some meta"); + /// ``` + pub fn with_doc(&mut self, doc: impl Into) -> &mut Self { + self.doc = doc.into(); + self + } + + /// Get a shared reference to the inner fields of this field, if it has any. + #[must_use] + pub fn fields(&self) -> Option<&Fields> { + self.fields.as_ref() + } + + /// Get a mutable reference to the inner fields of this field, if it has any. + pub fn fields_mut(&mut self) -> Option<&mut Fields> { + self.fields.as_mut() + } + + /// Return whether this field has inner fields. + /// + /// ``` + /// # use ron::ser::path_meta::{Field, Fields}; + /// + /// let mut field = Field::empty(); + /// + /// assert!(!field.has_fields()); + /// + /// field.with_fields(Some(Fields::default())); + /// + /// assert!(field.has_fields()); + /// ``` + #[must_use] + pub fn has_fields(&self) -> bool { + self.fields.is_some() + } + + /// Set the inner fields of this field. + /// + /// ``` + /// # use ron::ser::path_meta::{Field, Fields}; + /// + /// let mut field = Field::empty(); + /// + /// assert!(!field.has_fields()); + /// + /// field.with_fields(Some(Fields::default())); + /// + /// assert!(field.has_fields()); + /// + /// field.with_fields(None); + /// + /// assert!(!field.has_fields()); + /// ``` + pub fn with_fields(&mut self, fields: Option) -> &mut Self { + self.fields = fields; + self + } + + /// Ergonomic shortcut for building some inner fields. + /// + /// ``` + /// # use ron::ser::path_meta::Field; + /// + /// let mut field = Field::empty(); + /// + /// field.build_fields(|fields| { + /// fields.field("inner field"); + /// }); + /// + /// assert_eq!(field.fields().map(|fields| fields.contains("inner field")), Some(true)); + /// ``` + pub fn build_fields(&mut self, builder: impl FnOnce(&mut Fields)) -> &mut Self { + let mut fields = Fields::default(); + builder(&mut fields); + self.with_fields(Some(fields)); + self + } +} + +/// Mapping of names to [`Field`]s. +#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)] +pub struct Fields { + fields: HashMap, +} + +impl Fields { + /// Return a new, empty metadata field map. + #[must_use] + pub fn new() -> Self { + Self::default() + } + + /// Return whether this field map contains no fields. + /// + /// ``` + /// # use ron::ser::path_meta::{Fields, Field}; + /// + /// let mut fields = Fields::default(); + /// + /// assert!(fields.is_empty()); + /// + /// fields.insert("", Field::empty()); + /// + /// assert!(!fields.is_empty()); + /// ``` + #[must_use] + pub fn is_empty(&self) -> bool { + self.fields.is_empty() + } + + /// Return whether this field map contains a field with the given name. + /// + /// ``` + /// # use ron::ser::path_meta::{Fields, Field}; + /// + /// let fields: Fields = [("a thing", Field::empty())].into_iter().collect(); + /// + /// assert!(fields.contains("a thing")); + /// assert!(!fields.contains("not a thing")); + /// ``` + pub fn contains(&self, name: impl AsRef) -> bool { + self.fields.contains_key(name.as_ref()) + } + + /// Get a reference to the field with the provided `name`, if it exists. + /// + /// ``` + /// # use ron::ser::path_meta::{Fields, Field}; + /// + /// let fields: Fields = [("a thing", Field::empty())].into_iter().collect(); + /// + /// assert!(fields.get("a thing").is_some()); + /// assert!(fields.get("not a thing").is_none()); + /// ``` + pub fn get(&self, name: impl AsRef) -> Option<&Field> { + self.fields.get(name.as_ref()) + } + + /// Get a mutable reference to the field with the provided `name`, if it exists. + /// + /// ``` + /// # use ron::ser::path_meta::{Fields, Field}; + /// + /// let mut fields: Fields = [("a thing", Field::empty())].into_iter().collect(); + /// + /// assert!(fields.get_mut("a thing").is_some()); + /// assert!(fields.get_mut("not a thing").is_none()); + /// ``` + pub fn get_mut(&mut self, name: impl AsRef) -> Option<&mut Field> { + self.fields.get_mut(name.as_ref()) + } + + /// Insert a field with the given name into the map. + /// + /// ``` + /// # use ron::ser::path_meta::{Fields, Field}; + /// + /// let mut fields = Fields::default(); + /// + /// assert!(fields.insert("field", Field::empty()).is_none()); + /// assert!(fields.insert("field", Field::empty()).is_some()); + /// ``` + pub fn insert(&mut self, name: impl Into, field: Field) -> Option { + self.fields.insert(name.into(), field) + } + + /// Remove a field with the given name from the map. + /// + /// ``` + /// # use ron::ser::path_meta::{Fields, Field}; + /// + /// let mut fields: Fields = [("a", Field::empty())].into_iter().collect(); + /// + /// assert_eq!(fields.remove("a"), Some(Field::empty())); + /// assert_eq!(fields.remove("a"), None); + /// ``` + pub fn remove(&mut self, name: impl AsRef) -> Option { + self.fields.remove(name.as_ref()) + } + + /// Get a mutable reference to the field with the provided `name`, + /// inserting an empty [`Field`] if it didn't exist. + /// + /// ``` + /// # use ron::ser::path_meta::Fields; + /// + /// let mut fields = Fields::default(); + /// + /// assert!(!fields.contains("thing")); + /// + /// fields.field("thing"); + /// + /// assert!(fields.contains("thing")); + /// ``` + pub fn field(&mut self, name: impl Into) -> &mut Field { + self.fields.entry(name.into()).or_insert_with(Field::empty) + } +} + +impl> FromIterator<(K, Field)> for Fields { + fn from_iter>(iter: T) -> Self { + Self { + fields: iter.into_iter().map(|(k, v)| (k.into(), v)).collect(), + } + } +} diff --git a/third_party/rust/ron/src/ser/raw.rs b/third_party/rust/ron/src/ser/raw.rs new file mode 100644 index 000000000000..13e88e8b6d50 --- /dev/null +++ b/third_party/rust/ron/src/ser/raw.rs @@ -0,0 +1,240 @@ +use std::fmt; + +use serde::{ser, Serialize}; + +use super::{Error, Result}; + +pub struct Serializer<'a, W: fmt::Write> { + ser: &'a mut super::Serializer, +} + +impl<'a, W: fmt::Write> Serializer<'a, W> { + pub fn new(ser: &'a mut super::Serializer) -> Self { + Self { ser } + } +} + +impl<'a, W: fmt::Write> ser::Serializer for Serializer<'a, W> { + type Error = Error; + type Ok = (); + type SerializeMap = ser::Impossible<(), Error>; + type SerializeSeq = ser::Impossible<(), Error>; + type SerializeStruct = ser::Impossible<(), Error>; + type SerializeStructVariant = ser::Impossible<(), Error>; + type SerializeTuple = ser::Impossible<(), Error>; + type SerializeTupleStruct = ser::Impossible<(), Error>; + type SerializeTupleVariant = ser::Impossible<(), Error>; + + fn serialize_bool(self, _: bool) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_i8(self, _: i8) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_i16(self, _: i16) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_i32(self, _: i32) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_i64(self, _: i64) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + #[cfg(feature = "integer128")] + fn serialize_i128(self, _: i128) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_u8(self, _: u8) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_u16(self, _: u16) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_u32(self, _: u32) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_u64(self, _: u64) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + #[cfg(feature = "integer128")] + fn serialize_u128(self, _: u128) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_f32(self, _: f32) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_f64(self, _: f64) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_char(self, _: char) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_str(self, ron: &str) -> Result<()> { + self.ser.output.write_str(ron)?; + Ok(()) + } + + fn serialize_bytes(self, _: &[u8]) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_none(self) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_some(self, _: &T) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_unit(self) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_unit_struct(self, _: &'static str) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_unit_variant(self, _: &'static str, _: u32, _: &'static str) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_newtype_struct(self, _: &'static str, _: &T) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_newtype_variant( + self, + _: &'static str, + _: u32, + _: &'static str, + _: &T, + ) -> Result<()> { + Err(Error::ExpectedRawValue) + } + + fn serialize_seq(self, _: Option) -> Result { + Err(Error::ExpectedRawValue) + } + + fn serialize_tuple(self, _: usize) -> Result { + Err(Error::ExpectedRawValue) + } + + fn serialize_tuple_struct( + self, + _: &'static str, + _: usize, + ) -> Result { + Err(Error::ExpectedRawValue) + } + + fn serialize_tuple_variant( + self, + _: &'static str, + _: u32, + _: &'static str, + _: usize, + ) -> Result { + Err(Error::ExpectedRawValue) + } + + fn serialize_map(self, _: Option) -> Result { + Err(Error::ExpectedRawValue) + } + + fn serialize_struct(self, _: &'static str, _: usize) -> Result { + Err(Error::ExpectedRawValue) + } + + fn serialize_struct_variant( + self, + _: &'static str, + _: u32, + _: &'static str, + _: usize, + ) -> Result { + Err(Error::ExpectedRawValue) + } +} + +#[cfg(test)] +mod tests { + macro_rules! test_non_raw_value { + ($test_name:ident => $serialize_method:ident($($serialize_param:expr),*)) => { + #[test] + fn $test_name() { + use serde::{Serialize, Serializer}; + + struct Inner; + + impl Serialize for Inner { + fn serialize(&self, serializer: S) -> Result { + serializer.$serialize_method($($serialize_param),*).map(|_| unreachable!()) + } + } + + #[derive(Debug)] + struct Newtype; + + impl Serialize for Newtype { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_newtype_struct( + crate::value::raw::RAW_VALUE_TOKEN, &Inner, + ) + } + } + + assert_eq!( + crate::to_string(&Newtype).unwrap_err(), + crate::Error::ExpectedRawValue + ) + } + }; + } + + test_non_raw_value! { test_bool => serialize_bool(false) } + test_non_raw_value! { test_i8 => serialize_i8(0) } + test_non_raw_value! { test_i16 => serialize_i16(0) } + test_non_raw_value! { test_i32 => serialize_i32(0) } + test_non_raw_value! { test_i64 => serialize_i64(0) } + #[cfg(feature = "integer128")] + test_non_raw_value! { test_i128 => serialize_i128(0) } + test_non_raw_value! { test_u8 => serialize_u8(0) } + test_non_raw_value! { test_u16 => serialize_u16(0) } + test_non_raw_value! { test_u32 => serialize_u32(0) } + test_non_raw_value! { test_u64 => serialize_u64(0) } + #[cfg(feature = "integer128")] + test_non_raw_value! { test_u128 => serialize_u128(0) } + test_non_raw_value! { test_f32 => serialize_f32(0.0) } + test_non_raw_value! { test_f64 => serialize_f64(0.0) } + test_non_raw_value! { test_char => serialize_char('\0') } + test_non_raw_value! { test_bytes => serialize_bytes(b"") } + test_non_raw_value! { test_none => serialize_none() } + test_non_raw_value! { test_some => serialize_some(&()) } + test_non_raw_value! { test_unit => serialize_unit() } + test_non_raw_value! { test_unit_struct => serialize_unit_struct("U") } + test_non_raw_value! { test_unit_variant => serialize_unit_variant("E", 0, "U") } + test_non_raw_value! { test_newtype_struct => serialize_newtype_struct("N", &()) } + test_non_raw_value! { test_newtype_variant => serialize_newtype_variant("E", 0, "N", &()) } + test_non_raw_value! { test_seq => serialize_seq(None) } + test_non_raw_value! { test_tuple => serialize_tuple(0) } + test_non_raw_value! { test_tuple_struct => serialize_tuple_struct("T", 0) } + test_non_raw_value! { test_tuple_variant => serialize_tuple_variant("E", 0, "T", 0) } + test_non_raw_value! { test_map => serialize_map(None) } + test_non_raw_value! { test_struct => serialize_struct("S", 0) } + test_non_raw_value! { test_struct_variant => serialize_struct_variant("E", 0, "S", 0) } +} diff --git a/third_party/rust/ron/src/ser/tests.rs b/third_party/rust/ron/src/ser/tests.rs index fce7f0071074..4716e3840d23 100644 --- a/third_party/rust/ron/src/ser/tests.rs +++ b/third_party/rust/ron/src/ser/tests.rs @@ -1,6 +1,6 @@ use serde_derive::Serialize; -use super::to_string; +use crate::Number; #[derive(Serialize)] struct EmptyStruct1; @@ -8,6 +8,12 @@ struct EmptyStruct1; #[derive(Serialize)] struct EmptyStruct2 {} +#[derive(Serialize)] +struct NewType(i32); + +#[derive(Serialize)] +struct TupleStruct(f32, f32); + #[derive(Serialize)] struct MyStruct { x: f32, @@ -24,91 +30,101 @@ enum MyEnum { #[test] fn test_empty_struct() { - assert_eq!(to_string(&EmptyStruct1).unwrap(), "()"); - assert_eq!(to_string(&EmptyStruct2 {}).unwrap(), "()"); + check_to_string_writer(&EmptyStruct1, "()", "EmptyStruct1"); + check_to_string_writer(&EmptyStruct2 {}, "()", "EmptyStruct2()"); } #[test] fn test_struct() { let my_struct = MyStruct { x: 4.0, y: 7.0 }; - assert_eq!(to_string(&my_struct).unwrap(), "(x:4.0,y:7.0)"); + check_to_string_writer(&my_struct, "(x:4.0,y:7.0)", "MyStruct(x: 4.0, y: 7.0)"); - #[derive(Serialize)] - struct NewType(i32); + check_to_string_writer(&NewType(42), "(42)", "NewType(42)"); - assert_eq!(to_string(&NewType(42)).unwrap(), "(42)"); - - #[derive(Serialize)] - struct TupleStruct(f32, f32); - - assert_eq!(to_string(&TupleStruct(2.0, 5.0)).unwrap(), "(2.0,5.0)"); + check_to_string_writer(&TupleStruct(2.0, 5.0), "(2.0,5.0)", "TupleStruct(2.0, 5.0)"); } #[test] fn test_option() { - assert_eq!(to_string(&Some(1u8)).unwrap(), "Some(1)"); - assert_eq!(to_string(&None::).unwrap(), "None"); + check_to_string_writer(&Some(1u8), "Some(1)", "Some(1)"); + check_to_string_writer(&None::, "None", "None"); } #[test] fn test_enum() { - assert_eq!(to_string(&MyEnum::A).unwrap(), "A"); - assert_eq!(to_string(&MyEnum::B(true)).unwrap(), "B(true)"); - assert_eq!(to_string(&MyEnum::C(true, 3.5)).unwrap(), "C(true,3.5)"); - assert_eq!(to_string(&MyEnum::D { a: 2, b: 3 }).unwrap(), "D(a:2,b:3)"); + check_to_string_writer(&MyEnum::A, "A", "A"); + check_to_string_writer(&MyEnum::B(true), "B(true)", "B(true)"); + check_to_string_writer(&MyEnum::C(true, 3.5), "C(true,3.5)", "C(true, 3.5)"); + check_to_string_writer(&MyEnum::D { a: 2, b: 3 }, "D(a:2,b:3)", "D(a: 2, b: 3)"); } #[test] fn test_array() { let empty: [i32; 0] = []; - assert_eq!(to_string(&empty).unwrap(), "()"); + check_to_string_writer(&empty, "()", "()"); let empty_ref: &[i32] = ∅ - assert_eq!(to_string(&empty_ref).unwrap(), "[]"); + check_to_string_writer(&empty_ref, "[]", "[]"); - assert_eq!(to_string(&[2, 3, 4i32]).unwrap(), "(2,3,4)"); - assert_eq!(to_string(&(&[2, 3, 4i32] as &[i32])).unwrap(), "[2,3,4]"); + check_to_string_writer(&[2, 3, 4i32], "(2,3,4)", "(2, 3, 4)"); + check_to_string_writer( + &(&[2, 3, 4i32] as &[i32]), + "[2,3,4]", + "[\n 2,\n 3,\n 4,\n]", + ); } #[test] fn test_slice() { - assert_eq!(to_string(&[0, 1, 2, 3, 4, 5][..]).unwrap(), "[0,1,2,3,4,5]"); - assert_eq!(to_string(&[0, 1, 2, 3, 4, 5][1..4]).unwrap(), "[1,2,3]"); + check_to_string_writer( + &[0, 1, 2, 3, 4, 5][..], + "[0,1,2,3,4,5]", + "[\n 0,\n 1,\n 2,\n 3,\n 4,\n 5,\n]", + ); + check_to_string_writer( + &[0, 1, 2, 3, 4, 5][1..4], + "[1,2,3]", + "[\n 1,\n 2,\n 3,\n]", + ); } #[test] fn test_vec() { - assert_eq!(to_string(&vec![0, 1, 2, 3, 4, 5]).unwrap(), "[0,1,2,3,4,5]"); + check_to_string_writer( + &vec![0, 1, 2, 3, 4, 5], + "[0,1,2,3,4,5]", + "[\n 0,\n 1,\n 2,\n 3,\n 4,\n 5,\n]", + ); } #[test] fn test_map() { - use std::collections::HashMap; + use std::collections::BTreeMap; - let mut map = HashMap::new(); + let mut map = BTreeMap::new(); map.insert((true, false), 4); map.insert((false, false), 123); - let s = to_string(&map).unwrap(); - s.starts_with('{'); - s.contains("(true,false):4"); - s.contains("(false,false):123"); - s.ends_with('}'); + check_to_string_writer( + &map, + "{(false,false):123,(true,false):4}", + "{\n (false, false): 123,\n (true, false): 4,\n}", + ); } #[test] fn test_string() { - assert_eq!(to_string(&"Some string").unwrap(), "\"Some string\""); + check_to_string_writer(&"Some string", "\"Some string\"", "\"Some string\""); } #[test] fn test_char() { - assert_eq!(to_string(&'c').unwrap(), "'c'"); + check_to_string_writer(&'c', "'c'", "'c'"); } #[test] fn test_escape() { - assert_eq!(to_string(&r#""Quoted""#).unwrap(), r#""\"Quoted\"""#); + check_to_string_writer(&r#""Quoted""#, r#""\"Quoted\"""#, r#""\"Quoted\"""#); } #[test] @@ -116,19 +132,46 @@ fn test_byte_stream() { use serde_bytes; let small: [u8; 16] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; - assert_eq!( - to_string(&small).unwrap(), - "(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15)" + check_to_string_writer( + &small, + "(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15)", + "(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)", + ); + + let large = vec![0x01, 0x02, 0x03, 0x04]; + let large = serde_bytes::Bytes::new(&large); + check_to_string_writer( + &large, + "b\"\\x01\\x02\\x03\\x04\"", + "b\"\\x01\\x02\\x03\\x04\"", + ); + + let large = vec![0x01, 0x02, 0x03, 0x04, 0x05, 0x06]; + let large = serde_bytes::Bytes::new(&large); + check_to_string_writer( + &large, + "b\"\\x01\\x02\\x03\\x04\\x05\\x06\"", + "b\"\\x01\\x02\\x03\\x04\\x05\\x06\"", ); let large = vec![255u8; 64]; let large = serde_bytes::Bytes::new(&large); - assert_eq!( - to_string(&large).unwrap(), + check_to_string_writer( + &large, concat!( - "\"/////////////////////////////////////////", - "////////////////////////////////////////////w==\"" - ) + "b\"\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff", + "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff", + "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff", + "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff", + "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\"" + ), + concat!( + "b\"\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff", + "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff", + "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff", + "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff", + "\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\"" + ), ); } @@ -142,6 +185,122 @@ fn rename() { TriangleList, } - assert_eq!(to_string(&Foo::D2).unwrap(), "r#2d"); - assert_eq!(to_string(&Foo::TriangleList).unwrap(), "r#triangle-list"); + check_to_string_writer(&Foo::D2, "r#2d", "r#2d"); + check_to_string_writer(&Foo::TriangleList, "r#triangle-list", "r#triangle-list"); +} + +#[test] +fn test_any_number_precision() { + check_ser_any_number(1_u8); + check_ser_any_number(-1_i8); + check_ser_any_number(1_f32); + check_ser_any_number(-1_f32); + check_ser_any_number(0.3_f64); + + check_to_string_writer(&Number::new(f32::NAN), "NaN", "NaN"); + check_to_string_writer(&f32::NAN, "NaN", "NaN"); + check_to_string_writer(&Number::new(-f32::NAN), "-NaN", "-NaN"); + check_to_string_writer(&(-f32::NAN), "-NaN", "-NaN"); + check_to_string_writer(&Number::new(f32::INFINITY), "inf", "inf"); + check_to_string_writer(&f32::INFINITY, "inf", "inf"); + check_to_string_writer(&Number::new(f32::NEG_INFINITY), "-inf", "-inf"); + check_to_string_writer(&f32::NEG_INFINITY, "-inf", "-inf"); + + macro_rules! test_min_max { + ($ty:ty) => { + check_ser_any_number(<$ty>::MIN); + check_ser_any_number(<$ty>::MAX); + }; + ($($ty:ty),*) => { + $(test_min_max! { $ty })* + }; + } + + test_min_max! { i8, i16, i32, i64, u8, u16, u32, u64, f32, f64 } + #[cfg(feature = "integer128")] + test_min_max! { i128, u128 } +} + +fn check_ser_any_number + std::fmt::Display + serde::Serialize>(n: T) { + let mut fmt = format!("{}", n); + if !fmt.contains('.') && std::any::type_name::().contains('f') { + fmt.push_str(".0"); + } + + check_to_string_writer(&n.into(), &fmt, &fmt); + check_to_string_writer(&n, &fmt, &fmt); +} + +#[test] +fn recursion_limit() { + assert_eq!( + crate::Options::default() + .with_recursion_limit(0) + .to_string(&[42]), + Err(crate::Error::ExceededRecursionLimit), + ); + assert_eq!( + crate::Options::default() + .with_recursion_limit(1) + .to_string(&[42]) + .as_deref(), + Ok("(42)"), + ); + assert_eq!( + crate::Options::default() + .without_recursion_limit() + .to_string(&[42]) + .as_deref(), + Ok("(42)"), + ); + + assert_eq!( + crate::Options::default() + .with_recursion_limit(1) + .to_string(&[[42]]), + Err(crate::Error::ExceededRecursionLimit), + ); + assert_eq!( + crate::Options::default() + .with_recursion_limit(2) + .to_string(&[[42]]) + .as_deref(), + Ok("((42))"), + ); + assert_eq!( + crate::Options::default() + .without_recursion_limit() + .to_string(&[[42]]) + .as_deref(), + Ok("((42))"), + ); +} + +fn check_to_string_writer(val: &T, check: &str, check_pretty: &str) { + let ron_str = super::to_string(val).unwrap(); + assert_eq!(ron_str, check); + + let ron_str_pretty = super::to_string_pretty( + val, + super::PrettyConfig::default() + .struct_names(true) + .compact_structs(true), + ) + .unwrap(); + assert_eq!(ron_str_pretty, check_pretty); + + let mut ron_writer = std::ffi::OsString::new(); + super::to_writer(&mut ron_writer, val).unwrap(); + assert_eq!(ron_writer, check); + + let mut ron_writer_pretty = std::ffi::OsString::new(); + super::to_writer_pretty( + &mut ron_writer_pretty, + val, + super::PrettyConfig::default() + .struct_names(true) + .compact_structs(true), + ) + .unwrap(); + assert_eq!(ron_writer_pretty, check_pretty); } diff --git a/third_party/rust/ron/src/ser/value.rs b/third_party/rust/ron/src/ser/value.rs index 1a477589868a..87fdbce83869 100644 --- a/third_party/rust/ron/src/ser/value.rs +++ b/third_party/rust/ron/src/ser/value.rs @@ -1,6 +1,6 @@ use serde::ser::{Serialize, Serializer}; -use crate::value::{Number, Value}; +use crate::value::Value; impl Serialize for Value { fn serialize(&self, serializer: S) -> Result @@ -11,11 +11,11 @@ impl Serialize for Value { Value::Bool(b) => serializer.serialize_bool(b), Value::Char(c) => serializer.serialize_char(c), Value::Map(ref m) => Serialize::serialize(m, serializer), - Value::Number(Number::Float(ref f)) => serializer.serialize_f64(f.get()), - Value::Number(Number::Integer(i)) => serializer.serialize_i64(i), + Value::Number(ref number) => Serialize::serialize(number, serializer), Value::Option(Some(ref o)) => serializer.serialize_some(o.as_ref()), Value::Option(None) => serializer.serialize_none(), Value::String(ref s) => serializer.serialize_str(s), + Value::Bytes(ref b) => serializer.serialize_bytes(b), Value::Seq(ref s) => Serialize::serialize(s, serializer), Value::Unit => serializer.serialize_unit(), } diff --git a/third_party/rust/ron/src/value.rs b/third_party/rust/ron/src/value.rs deleted file mode 100644 index ba843bc16a76..000000000000 --- a/third_party/rust/ron/src/value.rs +++ /dev/null @@ -1,619 +0,0 @@ -//! Value module. - -use std::{ - cmp::{Eq, Ordering}, - hash::{Hash, Hasher}, - iter::FromIterator, - ops::{Index, IndexMut}, -}; - -use serde::{ - de::{DeserializeOwned, DeserializeSeed, Deserializer, MapAccess, SeqAccess, Visitor}, - forward_to_deserialize_any, -}; -use serde_derive::{Deserialize, Serialize}; - -use crate::{de::Error, error::Result}; - -/// A [`Value`] to [`Value`] map. -/// -/// This structure either uses a [BTreeMap](std::collections::BTreeMap) or the -/// [IndexMap](indexmap::IndexMap) internally. -/// The latter can be used by enabling the `indexmap` feature. This can be used -/// to preserve the order of the parsed map. -#[derive(Clone, Debug, Default, Deserialize, Serialize)] -#[serde(transparent)] -pub struct Map(MapInner); - -impl Map { - /// Creates a new, empty [`Map`]. - pub fn new() -> Map { - Default::default() - } - - /// Returns the number of elements in the map. - pub fn len(&self) -> usize { - self.0.len() - } - - /// Returns `true` if `self.len() == 0`, `false` otherwise. - pub fn is_empty(&self) -> bool { - self.0.len() == 0 - } - - /// Inserts a new element, returning the previous element with this `key` if - /// there was any. - pub fn insert(&mut self, key: Value, value: Value) -> Option { - self.0.insert(key, value) - } - - /// Removes an element by its `key`. - pub fn remove(&mut self, key: &Value) -> Option { - self.0.remove(key) - } - - /// Iterate all key-value pairs. - pub fn iter(&self) -> impl Iterator + DoubleEndedIterator { - self.0.iter() - } - - /// Iterate all key-value pairs mutably. - pub fn iter_mut(&mut self) -> impl Iterator + DoubleEndedIterator { - self.0.iter_mut() - } - - /// Iterate all keys. - pub fn keys(&self) -> impl Iterator + DoubleEndedIterator { - self.0.keys() - } - - /// Iterate all values. - pub fn values(&self) -> impl Iterator + DoubleEndedIterator { - self.0.values() - } - - /// Iterate all values mutably. - pub fn values_mut(&mut self) -> impl Iterator + DoubleEndedIterator { - self.0.values_mut() - } - - /// Retains only the elements specified by the `keep` predicate. - /// - /// In other words, remove all pairs `(k, v)` for which `keep(&k, &mut v)` - /// returns `false`. - /// - /// The elements are visited in iteration order. - pub fn retain(&mut self, keep: F) - where - F: FnMut(&Value, &mut Value) -> bool, - { - self.0.retain(keep); - } -} - -impl FromIterator<(Value, Value)> for Map { - fn from_iter>(iter: T) -> Self { - Map(MapInner::from_iter(iter)) - } -} - -impl IntoIterator for Map { - type Item = (Value, Value); - - type IntoIter = ::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -/// Note: equality is only given if both values and order of values match -impl Eq for Map {} - -impl Hash for Map { - fn hash(&self, state: &mut H) { - self.iter().for_each(|x| x.hash(state)); - } -} - -impl Index<&Value> for Map { - type Output = Value; - - fn index(&self, index: &Value) -> &Self::Output { - &self.0[index] - } -} - -impl IndexMut<&Value> for Map { - fn index_mut(&mut self, index: &Value) -> &mut Self::Output { - self.0.get_mut(index).expect("no entry found for key") - } -} - -impl Ord for Map { - fn cmp(&self, other: &Map) -> Ordering { - self.iter().cmp(other.iter()) - } -} - -/// Note: equality is only given if both values and order of values match -impl PartialEq for Map { - fn eq(&self, other: &Map) -> bool { - self.iter().zip(other.iter()).all(|(a, b)| a == b) - } -} - -impl PartialOrd for Map { - fn partial_cmp(&self, other: &Map) -> Option { - self.iter().partial_cmp(other.iter()) - } -} - -#[cfg(not(feature = "indexmap"))] -type MapInner = std::collections::BTreeMap; -#[cfg(feature = "indexmap")] -type MapInner = indexmap::IndexMap; - -/// A wrapper for a number, which can be either [`f64`] or [`i64`]. -#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Hash, Ord)] -pub enum Number { - Integer(i64), - Float(Float), -} - -/// A wrapper for [`f64`], which guarantees that the inner value -/// is finite and thus implements [`Eq`], [`Hash`] and [`Ord`]. -#[derive(Copy, Clone, Debug)] -pub struct Float(f64); - -impl Float { - /// Construct a new [`Float`]. - pub fn new(v: f64) -> Self { - Float(v) - } - - /// Returns the wrapped float. - pub fn get(self) -> f64 { - self.0 - } -} - -impl Number { - /// Construct a new number. - pub fn new(v: impl Into) -> Self { - v.into() - } - - /// Returns the [`f64`] representation of the [`Number`] regardless of - /// whether the number is stored as a float or integer. - /// - /// # Example - /// - /// ``` - /// # use ron::value::Number; - /// let i = Number::new(5); - /// let f = Number::new(2.0); - /// assert_eq!(i.into_f64(), 5.0); - /// assert_eq!(f.into_f64(), 2.0); - /// ``` - pub fn into_f64(self) -> f64 { - self.map_to(|i| i as f64, |f| f) - } - - /// If the [`Number`] is a float, return it. Otherwise return [`None`]. - /// - /// # Example - /// - /// ``` - /// # use ron::value::Number; - /// let i = Number::new(5); - /// let f = Number::new(2.0); - /// assert_eq!(i.as_f64(), None); - /// assert_eq!(f.as_f64(), Some(2.0)); - /// ``` - pub fn as_f64(self) -> Option { - self.map_to(|_| None, Some) - } - - /// If the [`Number`] is an integer, return it. Otherwise return [`None`]. - /// - /// # Example - /// - /// ``` - /// # use ron::value::Number; - /// let i = Number::new(5); - /// let f = Number::new(2.0); - /// assert_eq!(i.as_i64(), Some(5)); - /// assert_eq!(f.as_i64(), None); - /// ``` - pub fn as_i64(self) -> Option { - self.map_to(Some, |_| None) - } - - /// Map this number to a single type using the appropriate closure. - /// - /// # Example - /// - /// ``` - /// # use ron::value::Number; - /// let i = Number::new(5); - /// let f = Number::new(2.0); - /// assert!(i.map_to(|i| i > 3, |f| f > 3.0)); - /// assert!(!f.map_to(|i| i > 3, |f| f > 3.0)); - /// ``` - pub fn map_to( - self, - integer_fn: impl FnOnce(i64) -> T, - float_fn: impl FnOnce(f64) -> T, - ) -> T { - match self { - Number::Integer(i) => integer_fn(i), - Number::Float(Float(f)) => float_fn(f), - } - } -} - -impl From for Number { - fn from(f: f64) -> Number { - Number::Float(Float(f)) - } -} - -impl From for Number { - fn from(i: i64) -> Number { - Number::Integer(i) - } -} - -impl From for Number { - fn from(i: i32) -> Number { - Number::Integer(i64::from(i)) - } -} - -/// The following [`Number`] conversion checks if the integer fits losslessly -/// into an [`i64`], before constructing a [`Number::Integer`] variant. -/// If not, the conversion defaults to [`Number::Float`]. - -impl From for Number { - fn from(i: u64) -> Number { - if i <= std::i64::MAX as u64 { - Number::Integer(i as i64) - } else { - Number::new(i as f64) - } - } -} - -/// Partial equality comparison -/// In order to be able to use [`Number`] as a mapping key, NaN floating values -/// wrapped in [`Float`] are equal to each other. It is not the case for -/// underlying [`f64`] values itself. -impl PartialEq for Float { - fn eq(&self, other: &Self) -> bool { - self.0.is_nan() && other.0.is_nan() || self.0 == other.0 - } -} - -/// Equality comparison -/// In order to be able to use [`Float`] as a mapping key, NaN floating values -/// wrapped in [`Float`] are equal to each other. It is not the case for -/// underlying [`f64`] values itself. -impl Eq for Float {} - -impl Hash for Float { - fn hash(&self, state: &mut H) { - state.write_u64(self.0.to_bits()); - } -} - -/// Partial ordering comparison -/// In order to be able to use [`Number`] as a mapping key, NaN floating values -/// wrapped in [`Number`] are equal to each other and are less then any other -/// floating value. It is not the case for the underlying [`f64`] values -/// themselves. -/// -/// ``` -/// use ron::value::Number; -/// assert!(Number::new(std::f64::NAN) < Number::new(std::f64::NEG_INFINITY)); -/// assert_eq!(Number::new(std::f64::NAN), Number::new(std::f64::NAN)); -/// ``` -impl PartialOrd for Float { - fn partial_cmp(&self, other: &Self) -> Option { - match (self.0.is_nan(), other.0.is_nan()) { - (true, true) => Some(Ordering::Equal), - (true, false) => Some(Ordering::Less), - (false, true) => Some(Ordering::Greater), - _ => self.0.partial_cmp(&other.0), - } - } -} - -/// Ordering comparison -/// In order to be able to use [`Float`] as a mapping key, NaN floating values -/// wrapped in [`Float`] are equal to each other and are less then any other -/// floating value. It is not the case for underlying [`f64`] values itself. -/// See the [`PartialEq`] implementation. -impl Ord for Float { - fn cmp(&self, other: &Self) -> Ordering { - self.partial_cmp(other).expect("Bug: Contract violation") - } -} - -#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] -pub enum Value { - Bool(bool), - Char(char), - Map(Map), - Number(Number), - Option(Option>), - String(String), - Seq(Vec), - Unit, -} - -impl Value { - /// Tries to deserialize this [`Value`] into `T`. - pub fn into_rust(self) -> Result - where - T: DeserializeOwned, - { - T::deserialize(self) - } -} - -/// Deserializer implementation for RON [`Value`]. -/// This does not support enums (because [`Value`] does not store them). -impl<'de> Deserializer<'de> for Value { - type Error = Error; - - forward_to_deserialize_any! { - bool f32 f64 char str string bytes - byte_buf option unit unit_struct newtype_struct seq tuple - tuple_struct map struct enum identifier ignored_any - } - - fn deserialize_any(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self { - Value::Bool(b) => visitor.visit_bool(b), - Value::Char(c) => visitor.visit_char(c), - Value::Map(m) => { - let old_len = m.len(); - - let mut items: Vec<(Value, Value)> = m.into_iter().collect(); - items.reverse(); - - let value = visitor.visit_map(MapAccessor { - items: &mut items, - value: None, - })?; - - if items.is_empty() { - Ok(value) - } else { - Err(Error::ExpectedDifferentLength { - expected: format!("a map of length {}", old_len - items.len()), - found: old_len, - }) - } - } - Value::Number(Number::Float(ref f)) => visitor.visit_f64(f.get()), - Value::Number(Number::Integer(i)) => visitor.visit_i64(i), - Value::Option(Some(o)) => visitor.visit_some(*o), - Value::Option(None) => visitor.visit_none(), - Value::String(s) => visitor.visit_string(s), - Value::Seq(mut seq) => { - let old_len = seq.len(); - - seq.reverse(); - let value = visitor.visit_seq(Seq { seq: &mut seq })?; - - if seq.is_empty() { - Ok(value) - } else { - Err(Error::ExpectedDifferentLength { - expected: format!("a sequence of length {}", old_len - seq.len()), - found: old_len, - }) - } - } - Value::Unit => visitor.visit_unit(), - } - } - - fn deserialize_i8(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_i64(visitor) - } - - fn deserialize_i16(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_i64(visitor) - } - - fn deserialize_i32(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_i64(visitor) - } - - fn deserialize_i64(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self { - Value::Number(Number::Integer(i)) => visitor.visit_i64(i), - v => Err(Error::Message(format!("Expected a number, got {:?}", v))), - } - } - - fn deserialize_u8(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_u64(visitor) - } - - fn deserialize_u16(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_u64(visitor) - } - - fn deserialize_u32(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - self.deserialize_u64(visitor) - } - - fn deserialize_u64(self, visitor: V) -> Result - where - V: Visitor<'de>, - { - match self { - Value::Number(Number::Integer(i)) => visitor.visit_u64(i as u64), - v => Err(Error::Message(format!("Expected a number, got {:?}", v))), - } - } -} - -struct MapAccessor<'a> { - items: &'a mut Vec<(Value, Value)>, - value: Option, -} - -impl<'a, 'de> MapAccess<'de> for MapAccessor<'a> { - type Error = Error; - - fn next_key_seed(&mut self, seed: K) -> Result> - where - K: DeserializeSeed<'de>, - { - // The `Vec` is reversed, so we can pop to get the originally first element - match self.items.pop() { - Some((key, value)) => { - self.value = Some(value); - seed.deserialize(key).map(Some) - } - None => Ok(None), - } - } - - fn next_value_seed(&mut self, seed: V) -> Result - where - V: DeserializeSeed<'de>, - { - match self.value.take() { - Some(value) => seed.deserialize(value), - None => panic!("Contract violation: value before key"), - } - } - - fn size_hint(&self) -> Option { - Some(self.items.len()) - } -} - -struct Seq<'a> { - seq: &'a mut Vec, -} - -impl<'a, 'de> SeqAccess<'de> for Seq<'a> { - type Error = Error; - - fn next_element_seed(&mut self, seed: T) -> Result> - where - T: DeserializeSeed<'de>, - { - // The `Vec` is reversed, so we can pop to get the originally first element - self.seq - .pop() - .map_or(Ok(None), |v| seed.deserialize(v).map(Some)) - } - - fn size_hint(&self) -> Option { - Some(self.seq.len()) - } -} - -#[cfg(test)] -mod tests { - use std::{collections::BTreeMap, fmt::Debug}; - - use serde::Deserialize; - - use super::*; - - fn assert_same<'de, T>(s: &'de str) - where - T: Debug + Deserialize<'de> + PartialEq, - { - use crate::de::from_str; - - let direct: T = from_str(s).unwrap(); - let value: Value = from_str(s).unwrap(); - let value = T::deserialize(value).unwrap(); - - assert_eq!(direct, value, "Deserialization for {:?} is not the same", s); - } - - #[test] - fn boolean() { - assert_same::("true"); - assert_same::("false"); - } - - #[test] - fn float() { - assert_same::("0.123"); - assert_same::("-4.19"); - } - - #[test] - fn int() { - assert_same::("626"); - assert_same::("-50"); - } - - #[test] - fn char() { - assert_same::("'4'"); - assert_same::("'c'"); - } - - #[test] - fn map() { - assert_same::>( - "{ -'a': \"Hello\", -'b': \"Bye\", - }", - ); - } - - #[test] - fn option() { - assert_same::>("Some('a')"); - assert_same::>("None"); - } - - #[test] - fn seq() { - assert_same::>("[1.0, 2.0, 3.0, 4.0]"); - } - - #[test] - fn unit() { - assert_same::<()>("()"); - } -} diff --git a/third_party/rust/ron/src/value/map.rs b/third_party/rust/ron/src/value/map.rs new file mode 100644 index 000000000000..8375184fad27 --- /dev/null +++ b/third_party/rust/ron/src/value/map.rs @@ -0,0 +1,287 @@ +use std::{ + cmp::{Eq, Ordering}, + hash::{Hash, Hasher}, + iter::FromIterator, + ops::{Index, IndexMut}, +}; + +use serde_derive::{Deserialize, Serialize}; + +use super::Value; + +/// A [`Value`] to [`Value`] map. +/// +/// This structure either uses a [`BTreeMap`](std::collections::BTreeMap) or the +/// [`IndexMap`](indexmap::IndexMap) internally. +/// The latter can be used by enabling the `indexmap` feature. This can be used +/// to preserve the order of the parsed map. +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +#[serde(transparent)] +pub struct Map(pub(crate) MapInner); + +#[cfg(not(feature = "indexmap"))] +type MapInner = std::collections::BTreeMap; +#[cfg(feature = "indexmap")] +type MapInner = indexmap::IndexMap; + +impl Map { + /// Creates a new, empty [`Map`]. + #[must_use] + pub fn new() -> Self { + Self::default() + } + + /// Returns the number of elements in the map. + #[must_use] + pub fn len(&self) -> usize { + self.0.len() + } + + /// Returns `true` if `self.len() == 0`, `false` otherwise. + #[must_use] + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Immutably looks up an element by its `key`. + #[must_use] + pub fn get(&self, key: &Value) -> Option<&Value> { + self.0.get(key) + } + + /// Mutably looks up an element by its `key`. + pub fn get_mut(&mut self, key: &Value) -> Option<&mut Value> { + self.0.get_mut(key) + } + + /// Inserts a new element, returning the previous element with this `key` if + /// there was any. + pub fn insert(&mut self, key: impl Into, value: impl Into) -> Option { + self.0.insert(key.into(), value.into()) + } + + /// Removes an element by its `key`. + pub fn remove(&mut self, key: &Value) -> Option { + #[cfg(feature = "indexmap")] + { + self.0.shift_remove(key) + } + #[cfg(not(feature = "indexmap"))] + { + self.0.remove(key) + } + } + + /// Iterate all key-value pairs. + #[must_use] + pub fn iter(&self) -> impl DoubleEndedIterator { + self.0.iter() + } + + /// Iterate all key-value pairs mutably. + #[must_use] + pub fn iter_mut(&mut self) -> impl DoubleEndedIterator { + self.0.iter_mut() + } + + /// Iterate all keys. + #[must_use] + pub fn keys(&self) -> impl DoubleEndedIterator { + self.0.keys() + } + + /// Iterate all values. + #[must_use] + pub fn values(&self) -> impl DoubleEndedIterator { + self.0.values() + } + + /// Iterate all values mutably. + #[must_use] + pub fn values_mut(&mut self) -> impl DoubleEndedIterator { + self.0.values_mut() + } + + /// Retains only the elements specified by the `keep` predicate. + /// + /// In other words, remove all pairs `(k, v)` for which `keep(&k, &mut v)` + /// returns `false`. + /// + /// The elements are visited in iteration order. + pub fn retain(&mut self, keep: F) + where + F: FnMut(&Value, &mut Value) -> bool, + { + self.0.retain(keep); + } +} + +impl Index<&Value> for Map { + type Output = Value; + + #[allow(clippy::expect_used)] + fn index(&self, index: &Value) -> &Self::Output { + self.get(index).expect("no entry found for key") + } +} + +impl IndexMut<&Value> for Map { + #[allow(clippy::expect_used)] + fn index_mut(&mut self, index: &Value) -> &mut Self::Output { + self.get_mut(index).expect("no entry found for key") + } +} + +impl IntoIterator for Map { + type Item = (Value, Value); + + type IntoIter = ::IntoIter; + + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl, V: Into> FromIterator<(K, V)> for Map { + fn from_iter>(iter: T) -> Self { + Map(iter + .into_iter() + .map(|(key, value)| (key.into(), value.into())) + .collect()) + } +} + +/// Note: equality is only given if both values and order of values match +impl PartialEq for Map { + fn eq(&self, other: &Map) -> bool { + self.cmp(other).is_eq() + } +} + +/// Note: equality is only given if both values and order of values match +impl Eq for Map {} + +impl PartialOrd for Map { + fn partial_cmp(&self, other: &Map) -> Option { + Some(self.cmp(other)) + } +} + +impl Ord for Map { + fn cmp(&self, other: &Map) -> Ordering { + self.iter().cmp(other.iter()) + } +} + +impl Hash for Map { + fn hash(&self, state: &mut H) { + self.iter().for_each(|x| x.hash(state)); + } +} + +#[cfg(test)] +mod tests { + use super::{Map, Value}; + + #[test] + fn map_usage() { + let mut map = Map::new(); + assert_eq!(map.len(), 0); + assert!(map.is_empty()); + + map.insert("a", 42); + assert_eq!(map.len(), 1); + assert!(!map.is_empty()); + + assert_eq!(map.keys().collect::>(), vec![&Value::from("a")]); + assert_eq!(map.values().collect::>(), vec![&Value::from(42)]); + assert_eq!( + map.iter().collect::>(), + vec![(&Value::from("a"), &Value::from(42))] + ); + + assert_eq!(map.get(&Value::from("a")), Some(&Value::from(42))); + assert_eq!(map.get(&Value::from("b")), None); + assert_eq!(map.get_mut(&Value::from("a")), Some(&mut Value::from(42))); + assert_eq!(map.get_mut(&Value::from("b")), None); + + map[&Value::from("a")] = Value::from(24); + assert_eq!(&map[&Value::from("a")], &Value::from(24)); + + for (key, value) in map.iter_mut() { + if key == &Value::from("a") { + *value = Value::from(42); + } + } + assert_eq!(&map[&Value::from("a")], &Value::from(42)); + + map.values_mut().for_each(|value| *value = Value::from(24)); + assert_eq!(&map[&Value::from("a")], &Value::from(24)); + + map.insert("b", 42); + assert_eq!(map.len(), 2); + assert!(!map.is_empty()); + assert_eq!(map.get(&Value::from("a")), Some(&Value::from(24))); + assert_eq!(map.get(&Value::from("b")), Some(&Value::from(42))); + + map.retain(|key, value| { + if key == &Value::from("a") { + *value = Value::from(42); + true + } else { + false + } + }); + assert_eq!(map.len(), 1); + assert_eq!(map.get(&Value::from("a")), Some(&Value::from(42))); + assert_eq!(map.get(&Value::from("b")), None); + + assert_eq!(map.remove(&Value::from("b")), None); + assert_eq!(map.remove(&Value::from("a")), Some(Value::from(42))); + assert_eq!(map.remove(&Value::from("a")), None); + } + + #[test] + fn map_hash() { + assert_same_hash(&Map::new(), &Map::new()); + assert_same_hash( + &[("a", 42)].into_iter().collect(), + &[("a", 42)].into_iter().collect(), + ); + assert_same_hash( + &[("b", 24), ("c", 42)].into_iter().collect(), + &[("b", 24), ("c", 42)].into_iter().collect(), + ); + } + + fn assert_same_hash(a: &Map, b: &Map) { + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + + assert_eq!(a, b); + assert!(a.cmp(b).is_eq()); + assert_eq!(a.partial_cmp(b), Some(std::cmp::Ordering::Equal)); + + let mut hasher = DefaultHasher::new(); + a.hash(&mut hasher); + let h1 = hasher.finish(); + + let mut hasher = DefaultHasher::new(); + b.hash(&mut hasher); + let h2 = hasher.finish(); + + assert_eq!(h1, h2); + } + + #[test] + #[should_panic(expected = "no entry found for key")] + fn map_index_panic() { + let _ = &Map::new()[&Value::Unit]; + } + + #[test] + #[should_panic(expected = "no entry found for key")] + fn map_index_mut_panic() { + let _ = &mut Map::new()[&Value::Unit]; + } +} diff --git a/third_party/rust/ron/src/value/mod.rs b/third_party/rust/ron/src/value/mod.rs new file mode 100644 index 000000000000..28ea83f98ac1 --- /dev/null +++ b/third_party/rust/ron/src/value/mod.rs @@ -0,0 +1,502 @@ +//! Value module. + +use std::{borrow::Cow, cmp::Eq, hash::Hash}; + +use serde::{ + de::{DeserializeOwned, DeserializeSeed, Deserializer, MapAccess, SeqAccess, Visitor}, + forward_to_deserialize_any, +}; + +use crate::{de::Error, error::Result}; + +mod map; +mod number; +pub(crate) mod raw; + +pub use map::Map; +pub use number::{Number, F32, F64}; +#[allow(clippy::useless_attribute, clippy::module_name_repetitions)] +pub use raw::RawValue; + +#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] +pub enum Value { + Bool(bool), + Char(char), + Map(Map), + Number(Number), + Option(Option>), + String(String), + Bytes(Vec), + Seq(Vec), + Unit, +} + +impl From for Value { + fn from(value: bool) -> Self { + Self::Bool(value) + } +} + +impl From for Value { + fn from(value: char) -> Self { + Self::Char(value) + } +} + +impl, V: Into> FromIterator<(K, V)> for Value { + fn from_iter>(iter: T) -> Self { + Self::Map(iter.into_iter().collect()) + } +} + +impl From for Value { + fn from(value: Map) -> Self { + Self::Map(value) + } +} + +impl> From for Value { + fn from(value: T) -> Self { + Self::Number(value.into()) + } +} + +impl> From> for Value { + fn from(value: Option) -> Self { + Self::Option(value.map(Into::into).map(Box::new)) + } +} + +impl<'a> From<&'a str> for Value { + fn from(value: &'a str) -> Self { + String::from(value).into() + } +} + +impl<'a> From> for Value { + fn from(value: Cow<'a, str>) -> Self { + String::from(value).into() + } +} + +impl From for Value { + fn from(value: String) -> Self { + Self::String(value) + } +} + +/// Special case to allow `Value::from(b"byte string")` +impl From<&'static [u8; N]> for Value { + fn from(value: &'static [u8; N]) -> Self { + Self::Bytes(Vec::from(*value)) + } +} + +impl> FromIterator for Value { + fn from_iter>(iter: I) -> Self { + Self::Seq(iter.into_iter().map(Into::into).collect()) + } +} + +impl<'a, T: Clone + Into> From<&'a [T]> for Value { + fn from(value: &'a [T]) -> Self { + value.iter().map(Clone::clone).map(Into::into).collect() + } +} + +impl> From> for Value { + fn from(value: Vec) -> Self { + value.into_iter().collect() + } +} + +impl From<()> for Value { + fn from(_value: ()) -> Self { + Value::Unit + } +} + +impl Value { + /// Tries to deserialize this [`Value`] into `T`. + pub fn into_rust(self) -> Result + where + T: DeserializeOwned, + { + T::deserialize(self) + } +} + +/// Deserializer implementation for RON [`Value`]. +/// This does not support enums (because [`Value`] does not store them). +impl<'de> Deserializer<'de> for Value { + type Error = Error; + + forward_to_deserialize_any! { + bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string bytes + byte_buf option unit unit_struct newtype_struct seq tuple + tuple_struct map struct enum identifier ignored_any + } + + #[cfg(feature = "integer128")] + forward_to_deserialize_any! { + i128 u128 + } + + fn deserialize_any(self, visitor: V) -> Result + where + V: Visitor<'de>, + { + match self { + Value::Bool(b) => visitor.visit_bool(b), + Value::Char(c) => visitor.visit_char(c), + Value::Map(m) => { + let old_len = m.len(); + + let mut items: Vec<(Value, Value)> = m.into_iter().collect(); + items.reverse(); + + let value = visitor.visit_map(MapAccessor { + items: &mut items, + value: None, + })?; + + if items.is_empty() { + Ok(value) + } else { + Err(Error::ExpectedDifferentLength { + expected: format!("a map of length {}", old_len - items.len()), + found: old_len, + }) + } + } + Value::Number(number) => number.visit(visitor), + Value::Option(Some(o)) => visitor.visit_some(*o), + Value::Option(None) => visitor.visit_none(), + Value::String(s) => visitor.visit_string(s), + Value::Bytes(b) => visitor.visit_byte_buf(b), + Value::Seq(mut seq) => { + let old_len = seq.len(); + + seq.reverse(); + let value = visitor.visit_seq(SeqAccessor { seq: &mut seq })?; + + if seq.is_empty() { + Ok(value) + } else { + Err(Error::ExpectedDifferentLength { + expected: format!("a sequence of length {}", old_len - seq.len()), + found: old_len, + }) + } + } + Value::Unit => visitor.visit_unit(), + } + } +} + +struct SeqAccessor<'a> { + seq: &'a mut Vec, +} + +impl<'a, 'de> SeqAccess<'de> for SeqAccessor<'a> { + type Error = Error; + + fn next_element_seed(&mut self, seed: T) -> Result> + where + T: DeserializeSeed<'de>, + { + // The `Vec` is reversed, so we can pop to get the originally first element + self.seq + .pop() + .map_or(Ok(None), |v| seed.deserialize(v).map(Some)) + } + + fn size_hint(&self) -> Option { + Some(self.seq.len()) + } +} + +struct MapAccessor<'a> { + items: &'a mut Vec<(Value, Value)>, + value: Option, +} + +impl<'a, 'de> MapAccess<'de> for MapAccessor<'a> { + type Error = Error; + + fn next_key_seed(&mut self, seed: K) -> Result> + where + K: DeserializeSeed<'de>, + { + // The `Vec` is reversed, so we can pop to get the originally first element + match self.items.pop() { + Some((key, value)) => { + self.value = Some(value); + seed.deserialize(key).map(Some) + } + None => Ok(None), + } + } + + #[allow(clippy::panic)] + fn next_value_seed(&mut self, seed: V) -> Result + where + V: DeserializeSeed<'de>, + { + match self.value.take() { + Some(value) => seed.deserialize(value), + None => panic!("Contract violation: value before key"), + } + } + + fn size_hint(&self) -> Option { + Some(self.items.len()) + } +} + +#[cfg(test)] +mod tests { + use std::{collections::BTreeMap, fmt::Debug}; + + use serde::Deserialize; + + use super::*; + + fn assert_same<'de, T>(s: &'de str) + where + T: Debug + Deserialize<'de> + PartialEq, + { + use crate::de::from_str; + + let direct: T = from_str(s).unwrap(); + let value: Value = from_str(s).unwrap(); + let de = T::deserialize(value.clone()).unwrap(); + + assert_eq!(direct, de, "Deserialization for {:?} is not the same", s); + + let value_roundtrip = Value::deserialize(value.clone()).unwrap(); + assert_eq!(value_roundtrip, value); + } + + fn assert_same_bytes<'de, T>(s: &'de [u8]) + where + T: Debug + Deserialize<'de> + PartialEq, + { + use crate::de::from_bytes; + + let direct: T = from_bytes(s).unwrap(); + let value: Value = from_bytes(s).unwrap(); + let de = T::deserialize(value.clone()).unwrap(); + + assert_eq!(direct, de, "Deserialization for {:?} is not the same", s); + + let value_roundtrip = Value::deserialize(value.clone()).unwrap(); + assert_eq!(value_roundtrip, value); + } + + #[test] + fn boolean() { + assert_same::("true"); + assert_same::("false"); + + assert_eq!(Value::from(true), Value::Bool(true)); + assert_eq!(Value::from(false), Value::Bool(false)); + } + + #[test] + fn float() { + assert_same::("0.123"); + assert_same::("-4.19"); + + assert_eq!( + Value::from(42_f32), + Value::Number(Number::F32(42_f32.into())) + ); + assert_eq!( + Value::from(42_f64), + Value::Number(Number::F64(42_f64.into())) + ); + } + + #[test] + fn int() { + assert_same::("626"); + assert_same::("-50"); + + assert_eq!(Value::from(0_i8), Value::Number(Number::I8(0))); + assert_eq!(Value::from(0_i16), Value::Number(Number::I16(0))); + assert_eq!(Value::from(0_i32), Value::Number(Number::I32(0))); + assert_eq!(Value::from(0_i64), Value::Number(Number::I64(0))); + #[cfg(feature = "integer128")] + assert_eq!(Value::from(0_i128), Value::Number(Number::I128(0))); + assert_eq!(Value::from(0_u8), Value::Number(Number::U8(0))); + assert_eq!(Value::from(0_u16), Value::Number(Number::U16(0))); + assert_eq!(Value::from(0_u32), Value::Number(Number::U32(0))); + assert_eq!(Value::from(0_u64), Value::Number(Number::U64(0))); + #[cfg(feature = "integer128")] + assert_eq!(Value::from(0_u128), Value::Number(Number::U128(0))); + } + + #[test] + fn char() { + assert_same::("'4'"); + assert_same::("'c'"); + + assert_eq!(Value::from('🦀'), Value::Char('🦀')); + } + + #[test] + fn string() { + assert_same::(r#""hello world""#); + assert_same::(r#""this is a Rusty 🦀 string""#); + assert_same::(r#""this is now valid UTF-8 \xf0\x9f\xa6\x80""#); + + assert_eq!(Value::from("slice"), Value::String(String::from("slice"))); + assert_eq!( + Value::from(String::from("string")), + Value::String(String::from("string")) + ); + assert_eq!( + Value::from(Cow::Borrowed("cow")), + Value::String(String::from("cow")) + ); + } + + #[test] + fn bytes() { + assert_same_bytes::(br#"b"hello world""#); + assert_same_bytes::( + br#"b"this is not valid UTF-8 \xf8\xa1\xa1\xa1\xa1""#, + ); + + assert_eq!(Value::from(b"bytes"), Value::Bytes(Vec::from(*b"bytes"))); + } + + #[test] + fn map() { + assert_same::>( + "{ +'a': \"Hello\", +'b': \"Bye\", + }", + ); + + assert_eq!(Value::from(Map::new()), Value::Map(Map::new())); + assert_eq!( + Value::from_iter([("a", 42)]), + Value::Map({ + let mut map = Map::new(); + map.insert(Value::from("a"), Value::from(42)); + map + }) + ); + } + + #[test] + fn option() { + assert_same::>("Some('a')"); + assert_same::>("None"); + + assert_eq!(Value::from(Option::::None), Value::Option(None)); + assert_eq!( + Value::from(Some(false)), + Value::Option(Some(Box::new(Value::Bool(false)))) + ); + assert_eq!( + Value::from(Some(Option::::None)), + Value::Option(Some(Box::new(Value::Option(None)))) + ); + } + + #[test] + fn seq() { + assert_same::>("[1.0, 2.0, 3.0, 4.0]"); + + assert_eq!( + Value::from([-1_i8, 2, -3].as_slice()), + Value::Seq(vec![ + Value::from(-1_i8), + Value::from(2_i8), + Value::from(-3_i8) + ]) + ); + assert_eq!( + Value::from(vec![-1_i8, 2, -3]), + Value::Seq(vec![ + Value::from(-1_i8), + Value::from(2_i8), + Value::from(-3_i8) + ]) + ); + assert_eq!( + Value::from_iter([-1_i8, 2, -3]), + Value::Seq(vec![ + Value::from(-1_i8), + Value::from(2_i8), + Value::from(-3_i8) + ]) + ); + } + + #[test] + fn unit() { + assert_same::<()>("()"); + + assert_eq!(Value::from(()), Value::Unit); + } + + #[test] + #[should_panic(expected = "Contract violation: value before key")] + fn map_access_contract_violation() { + struct BadVisitor; + + impl<'de> Visitor<'de> for BadVisitor { + type Value = (); + + // GRCOV_EXCL_START + fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.write_str("a map") + } + // GRCOV_EXCL_STOP + + fn visit_map>( + self, + mut map: A, + ) -> Result { + map.next_value::<()>() + } + } + + let value = Value::Map([("a", 42)].into_iter().collect()); + let _ = value.deserialize_map(BadVisitor); + } + + #[test] + fn transparent_value_newtype() { + struct NewtypeDeserializer; + + impl<'de> Deserializer<'de> for NewtypeDeserializer { + type Error = Error; + + fn deserialize_any>(self, visitor: V) -> Result { + visitor.visit_newtype_struct(serde::de::value::CharDeserializer::new('🦀')) + } + + // GRCOV_EXCL_START + forward_to_deserialize_any! { + bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 char str string + bytes byte_buf option unit unit_struct newtype_struct seq tuple + tuple_struct map struct enum identifier ignored_any + } + + #[cfg(feature = "integer128")] + forward_to_deserialize_any! { i128 u128 } + // GRCOV_EXCL_STOP + } + + assert_eq!( + Value::deserialize(NewtypeDeserializer).unwrap(), + Value::from('🦀') + ); + } +} diff --git a/third_party/rust/ron/src/value/number.rs b/third_party/rust/ron/src/value/number.rs new file mode 100644 index 000000000000..b38016b55285 --- /dev/null +++ b/third_party/rust/ron/src/value/number.rs @@ -0,0 +1,285 @@ +use std::{ + cmp::{Eq, Ordering}, + hash::{Hash, Hasher}, +}; + +use serde::{de::Visitor, Serialize, Serializer}; + +/// A wrapper for any numeric primitive type in Rust +#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Hash, Ord)] +pub enum Number { + I8(i8), + I16(i16), + I32(i32), + I64(i64), + #[cfg(feature = "integer128")] + I128(i128), + U8(u8), + U16(u16), + U32(u32), + U64(u64), + #[cfg(feature = "integer128")] + U128(u128), + F32(F32), + F64(F64), +} + +impl Serialize for Number { + fn serialize(&self, serializer: S) -> Result { + match self { + Self::I8(v) => serializer.serialize_i8(*v), + Self::I16(v) => serializer.serialize_i16(*v), + Self::I32(v) => serializer.serialize_i32(*v), + Self::I64(v) => serializer.serialize_i64(*v), + #[cfg(feature = "integer128")] + Self::I128(v) => serializer.serialize_i128(*v), + Self::U8(v) => serializer.serialize_u8(*v), + Self::U16(v) => serializer.serialize_u16(*v), + Self::U32(v) => serializer.serialize_u32(*v), + Self::U64(v) => serializer.serialize_u64(*v), + #[cfg(feature = "integer128")] + Self::U128(v) => serializer.serialize_u128(*v), + Self::F32(v) => serializer.serialize_f32(v.get()), + Self::F64(v) => serializer.serialize_f64(v.get()), + } + } +} + +impl Number { + pub fn visit<'de, V: Visitor<'de>, E: serde::de::Error>( + &self, + visitor: V, + ) -> Result { + match self { + Self::I8(v) => visitor.visit_i8(*v), + Self::I16(v) => visitor.visit_i16(*v), + Self::I32(v) => visitor.visit_i32(*v), + Self::I64(v) => visitor.visit_i64(*v), + #[cfg(feature = "integer128")] + Self::I128(v) => visitor.visit_i128(*v), + Self::U8(v) => visitor.visit_u8(*v), + Self::U16(v) => visitor.visit_u16(*v), + Self::U32(v) => visitor.visit_u32(*v), + Self::U64(v) => visitor.visit_u64(*v), + #[cfg(feature = "integer128")] + Self::U128(v) => visitor.visit_u128(*v), + Self::F32(v) => visitor.visit_f32(v.get()), + Self::F64(v) => visitor.visit_f64(v.get()), + } + } +} + +macro_rules! float_ty { + ($ty:ident($float:ty)) => { + #[doc = concat!( + "A wrapper for [`", stringify!($float), "`], which implements [`Eq`], ", + "[`Hash`] and [`Ord`] using [`", stringify!($float), "::total_cmp`] ", + "for a total order comparison", + )] + #[derive(Copy, Clone, Debug)] // GRCOV_EXCL_LINE + pub struct $ty(pub $float); + + impl $ty { + #[doc = concat!("Construct a new [`", stringify!($ty), "`].")] + #[must_use] + pub fn new(v: $float) -> Self { + Self(v) + } + + #[doc = concat!("Returns the wrapped [`", stringify!($float), "`].")] + #[must_use] + pub fn get(self) -> $float { + self.0 + } + } + + impl From<$float> for $ty { + fn from(v: $float) -> Self { + Self::new(v) + } + } + + /// Partial equality comparison + /// + #[doc = concat!( + "In order to be able to use [`", stringify!($ty), "`] as a mapping key, ", + "floating values use [`", stringify!($float), "::total_cmp`] for a total ", + "order comparison.", + )] + /// + /// See the [`Ord`] implementation. + impl PartialEq for $ty { + fn eq(&self, other: &Self) -> bool { + self.cmp(other).is_eq() + } + } + + /// Equality comparison + /// + #[doc = concat!( + "In order to be able to use [`", stringify!($ty), "`] as a mapping key, ", + "floating values use [`", stringify!($float), "::total_cmp`] for a total ", + "order comparison.", + )] + /// + /// See the [`Ord`] implementation. + impl Eq for $ty {} + + impl Hash for $ty { + fn hash(&self, state: &mut H) { + self.0.to_bits().hash(state); + } + } + + /// Partial ordering comparison + /// + #[doc = concat!( + "In order to be able to use [`", stringify!($ty), "`] as a mapping key, ", + "floating values use [`", stringify!($float), "::total_cmp`] for a total ", + "order comparison.", + )] + /// + /// See the [`Ord`] implementation. + impl PartialOrd for $ty { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } + } + + /// Ordering comparison + /// + #[doc = concat!( + "In order to be able to use [`", stringify!($ty), "`] as a mapping key, ", + "floating values use [`", stringify!($float), "::total_cmp`] for a total ", + "order comparison.", + )] + /// + /// ``` + #[doc = concat!("use ron::value::", stringify!($ty), ";")] + #[doc = concat!( + "assert!(", stringify!($ty), "::new(", stringify!($float), "::NAN) > ", + stringify!($ty), "::new(", stringify!($float), "::INFINITY));", + )] + #[doc = concat!( + "assert!(", stringify!($ty), "::new(-", stringify!($float), "::NAN) < ", + stringify!($ty), "::new(", stringify!($float), "::NEG_INFINITY));", + )] + #[doc = concat!( + "assert!(", stringify!($ty), "::new(", stringify!($float), "::NAN) == ", + stringify!($ty), "::new(", stringify!($float), "::NAN));", + )] + /// ``` + impl Ord for $ty { + fn cmp(&self, other: &Self) -> Ordering { + self.0.total_cmp(&other.0) + } + } + }; +} + +float_ty! { F32(f32) } +float_ty! { F64(f64) } + +impl Number { + /// Construct a new number. + pub fn new(v: impl Into) -> Self { + v.into() + } + + /// Returns the [`f64`] representation of the [`Number`] regardless of + /// whether the number is stored as a float or integer. + /// + /// # Example + /// + /// ``` + /// # use ron::value::Number; + /// let i = Number::new(5); + /// let f = Number::new(2.0); + /// assert_eq!(i.into_f64(), 5.0); + /// assert_eq!(f.into_f64(), 2.0); + /// ``` + #[must_use] + pub fn into_f64(self) -> f64 { + #[allow(clippy::cast_precision_loss)] + match self { + Number::I8(v) => f64::from(v), + Number::I16(v) => f64::from(v), + Number::I32(v) => f64::from(v), + Number::I64(v) => v as f64, + #[cfg(feature = "integer128")] + Number::I128(v) => v as f64, + Number::U8(v) => f64::from(v), + Number::U16(v) => f64::from(v), + Number::U32(v) => f64::from(v), + Number::U64(v) => v as f64, + #[cfg(feature = "integer128")] + Number::U128(v) => v as f64, + Number::F32(v) => f64::from(v.get()), + Number::F64(v) => v.get(), + } + } +} + +macro_rules! number_from_impl { + (Number::$variant:ident($wrap:ident($ty:ty))) => { + impl From<$ty> for Number { + fn from(v: $ty) -> Number { + Number::$variant($wrap(v)) + } + } + }; + (Number::$variant:ident($ty:ty)) => { + impl From<$ty> for Number { + fn from(v: $ty) -> Number { + Number::$variant(v) + } + } + }; +} + +number_from_impl! { Number::I8(i8) } +number_from_impl! { Number::I16(i16) } +number_from_impl! { Number::I32(i32) } +number_from_impl! { Number::I64(i64) } +#[cfg(feature = "integer128")] +number_from_impl! { Number::I128(i128) } +number_from_impl! { Number::U8(u8) } +number_from_impl! { Number::U16(u16) } +number_from_impl! { Number::U32(u32) } +number_from_impl! { Number::U64(u64) } +#[cfg(feature = "integer128")] +number_from_impl! { Number::U128(u128) } +number_from_impl! { Number::F32(F32(f32)) } +number_from_impl! { Number::F64(F64(f64)) } + +#[cfg(test)] +mod tests { + use std::collections::hash_map::DefaultHasher; + use std::hash::{Hash, Hasher}; + + use super::*; + + fn hash(v: &T) -> u64 { + let mut state = DefaultHasher::new(); + v.hash(&mut state); + state.finish() + } + + #[test] + fn test_nan() { + assert_eq!(F32(f32::NAN), F32(f32::NAN)); + assert_eq!(F32(-f32::NAN), F32(-f32::NAN)); + assert_ne!(F32(f32::NAN), F32(-f32::NAN)); + + assert_eq!(hash(&F32(f32::NAN)), hash(&F32(f32::NAN))); + assert_eq!(hash(&F32(-f32::NAN)), hash(&F32(-f32::NAN))); + assert_ne!(hash(&F32(f32::NAN)), hash(&F32(-f32::NAN))); + } + + #[test] + fn test_partial_ord() { + assert!(F32(f32::NAN) > F32(f32::INFINITY)); + assert!(F32(-f32::NAN) < F32(f32::NEG_INFINITY)); + assert!(F32(f32::NAN) == F32(f32::NAN)); + } +} diff --git a/third_party/rust/ron/src/value/raw.rs b/third_party/rust/ron/src/value/raw.rs new file mode 100644 index 000000000000..1dabe6e76a4b --- /dev/null +++ b/third_party/rust/ron/src/value/raw.rs @@ -0,0 +1,250 @@ +// Inspired by David Tolnay's serde-rs +// https://github.com/serde-rs/json/blob/master/src/raw.rs +// Licensed under either of Apache License, Version 2.0 or MIT license at your option. + +use std::{fmt, ops::Range}; + +use serde::{de, ser, Deserialize, Serialize}; + +use crate::{ + error::{Error, SpannedResult}, + options::Options, +}; + +// NOTE: Keep synchronised with fuzz/arbitrary::typed_data::RAW_VALUE_TOKEN +pub(crate) const RAW_VALUE_TOKEN: &str = "$ron::private::RawValue"; + +#[allow(clippy::module_name_repetitions)] +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)] +#[repr(transparent)] +pub struct RawValue { + ron: str, +} + +#[allow(unsafe_code)] +impl RawValue { + fn from_borrowed_str(ron: &str) -> &Self { + // Safety: RawValue is a transparent newtype around str + unsafe { &*(ron as *const str as *const RawValue) } + } + + fn from_boxed_str(ron: Box) -> Box { + // Safety: RawValue is a transparent newtype around str + unsafe { std::mem::transmute::, Box>(ron) } + } + + fn into_boxed_str(raw_value: Box) -> Box { + // Safety: RawValue is a transparent newtype around str + unsafe { std::mem::transmute::, Box>(raw_value) } + } + + #[allow(clippy::expect_used)] + fn trim_range(ron: &str) -> Range { + fn trim_range_inner(ron: &str) -> Result, Error> { + let mut deserializer = crate::Deserializer::from_str(ron).map_err(Error::from)?; + + deserializer.parser.skip_ws()?; + let start_offset = ron.len() - deserializer.parser.src().len(); + + let _ = serde::de::IgnoredAny::deserialize(&mut deserializer)?; + + deserializer.parser.skip_ws()?; + let end_offset = deserializer.parser.pre_ws_src().len(); + + Ok(start_offset..(ron.len() - end_offset)) + } + + trim_range_inner(ron).expect("RawValue must contain valid ron") + } +} + +impl Clone for Box { + fn clone(&self) -> Self { + (**self).to_owned() + } +} + +impl ToOwned for RawValue { + type Owned = Box; + + fn to_owned(&self) -> Self::Owned { + RawValue::from_boxed_str(self.ron.to_owned().into_boxed_str()) + } +} + +impl fmt::Debug for RawValue { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("RawValue") + .field(&format_args!("{}", &self.ron)) + .finish() + } +} + +impl fmt::Display for RawValue { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(&self.ron) + } +} + +impl RawValue { + /// Get the inner raw RON string, which is guaranteed to contain valid RON. + #[must_use] + pub fn get_ron(&self) -> &str { + &self.ron + } + + /// Helper function to validate a RON string and turn it into a + /// [`RawValue`]. + pub fn from_ron(ron: &str) -> SpannedResult<&Self> { + let mut deserializer = crate::Deserializer::from_str(ron)?; + + // raw values can be used everywhere but extensions cannot + if !deserializer.extensions().is_empty() { + return Err(deserializer.span_error(Error::Message(String::from( + "ron::value::RawValue cannot enable extensions", + )))); + } + + let _ = <&Self>::deserialize(&mut deserializer).map_err(|e| deserializer.span_error(e))?; + + deserializer.end().map_err(|e| deserializer.span_error(e))?; + + Ok(Self::from_borrowed_str(ron)) + } + + /// Helper function to validate a RON string and turn it into a + /// [`RawValue`]. + pub fn from_boxed_ron(ron: Box) -> SpannedResult> { + match Self::from_ron(&ron) { + Ok(_) => Ok(Self::from_boxed_str(ron)), + Err(err) => Err(err), + } + } + + /// Helper function to deserialize the inner RON string into `T`. + pub fn into_rust<'de, T: Deserialize<'de>>(&'de self) -> SpannedResult { + Options::default().from_str(&self.ron) + } + + /// Helper function to serialize `value` into a RON string. + pub fn from_rust(value: &T) -> Result, Error> { + let ron = Options::default().to_string(value)?; + + Ok(RawValue::from_boxed_str(ron.into_boxed_str())) + } +} + +impl RawValue { + #[must_use] + /// Trims any leadning and trailing whitespace off the raw RON string, + /// including whitespace characters and comments. + pub fn trim(&self) -> &Self { + Self::from_borrowed_str(&self.ron[RawValue::trim_range(&self.ron)]) + } + + #[must_use] + #[allow(unsafe_code)] + /// Trims any leadning and trailing whitespace off the boxed raw RON string, + /// including whitespace characters and comments. + pub fn trim_boxed(self: Box) -> Box { + let trim_range = RawValue::trim_range(&self.ron); + let mut boxed_ron = RawValue::into_boxed_str(self).into_string(); + // SAFETY: ron[trim_range] is a valid str, so draining everything + // before and after leaves a valid str + unsafe { + boxed_ron.as_mut_vec().drain(trim_range.end..); + boxed_ron.as_mut_vec().drain(0..trim_range.start); + } + RawValue::from_boxed_str(boxed_ron.into_boxed_str()) + } +} + +impl From> for Box { + fn from(raw_value: Box) -> Self { + RawValue::into_boxed_str(raw_value) + } +} + +impl Serialize for RawValue { + fn serialize(&self, serializer: S) -> Result { + serializer.serialize_newtype_struct(RAW_VALUE_TOKEN, &self.ron) + } +} + +impl<'de: 'a, 'a> Deserialize<'de> for &'a RawValue { + fn deserialize>(deserializer: D) -> Result { + struct ReferenceVisitor; + + impl<'de> de::Visitor<'de> for ReferenceVisitor { + type Value = &'de RawValue; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + // This error message only shows up with foreign Deserializers + write!(formatter, "any valid borrowed RON-value-string") + } + + fn visit_borrowed_str(self, ron: &'de str) -> Result { + match Options::default().from_str::(ron) { + Ok(_) => Ok(RawValue::from_borrowed_str(ron)), + Err(err) => Err(de::Error::custom(format!( + "invalid RON value at {}: {}", + err.position, err.code + ))), + } + } + + fn visit_newtype_struct>( + self, + deserializer: D, + ) -> Result { + deserializer.deserialize_str(self) + } + } + + deserializer.deserialize_newtype_struct(RAW_VALUE_TOKEN, ReferenceVisitor) + } +} + +impl<'de> Deserialize<'de> for Box { + fn deserialize>(deserializer: D) -> Result { + struct BoxedVisitor; + + impl<'de> de::Visitor<'de> for BoxedVisitor { + type Value = Box; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + // This error message only shows up with foreign Deserializers + write!(formatter, "any valid RON-value-string") + } + + fn visit_str(self, ron: &str) -> Result { + match Options::default().from_str::(ron) { + Ok(_) => Ok(RawValue::from_boxed_str(ron.to_owned().into_boxed_str())), + Err(err) => Err(de::Error::custom(format!( + "invalid RON value at {}: {}", + err.position, err.code + ))), + } + } + + fn visit_string(self, ron: String) -> Result { + match Options::default().from_str::(&ron) { + Ok(_) => Ok(RawValue::from_boxed_str(ron.into_boxed_str())), + Err(err) => Err(de::Error::custom(format!( + "invalid RON value at {}: {}", + err.position, err.code + ))), + } + } + + fn visit_newtype_struct>( + self, + deserializer: D, + ) -> Result { + deserializer.deserialize_string(self) + } + } + + deserializer.deserialize_newtype_struct(RAW_VALUE_TOKEN, BoxedVisitor) + } +} diff --git a/third_party/rust/ron/tests/115_minimal_flattening.rs b/third_party/rust/ron/tests/115_minimal_flattening.rs new file mode 100644 index 000000000000..b44c76305b48 --- /dev/null +++ b/third_party/rust/ron/tests/115_minimal_flattening.rs @@ -0,0 +1,351 @@ +use std::collections::HashMap; + +use serde::{Deserialize, Serialize}; + +#[derive(Deserialize, Serialize, PartialEq, Eq, Debug)] +struct Main { + #[serde(flatten)] + required: Required, + #[serde(flatten)] + optional: Optional, + + some_other_field: u32, +} + +#[derive(Deserialize, Serialize, PartialEq, Eq, Debug)] +struct Required { + first: u32, + second: u32, +} + +#[derive(Deserialize, Serialize, PartialEq, Eq, Debug)] +struct Optional { + third: Option, +} + +#[derive(Deserialize, Serialize, PartialEq, Eq, Debug)] +struct MyType { + first: u32, + second: u32, + #[serde(flatten)] + everything_else: HashMap, +} + +#[derive(Deserialize, Serialize, PartialEq, Eq, Debug)] +struct AllOptional { + #[serde(flatten)] + everything_else: HashMap, +} + +#[derive(Deserialize, Serialize, PartialEq, Eq, Debug)] +enum Newtype { + Main(Main), + MyType(MyType), + AllOptional(AllOptional), +} + +#[test] +fn test_flatten_struct_into_struct() { + let val = Main { + required: Required { + first: 1, + second: 2, + }, + optional: Optional { third: Some(3) }, + some_other_field: 1337, + }; + + let ron = ron::ser::to_string_pretty(&val, ron::ser::PrettyConfig::default()).unwrap(); + + assert_eq!( + ron, + "{ + \"first\": 1, + \"second\": 2, + \"third\": Some(3), + \"some_other_field\": 1337, +}" + ); + + let de: Main = ron::from_str(&ron).unwrap(); + + assert_eq!(de, val); + + let val = Newtype::Main(Main { + required: Required { + first: 1, + second: 2, + }, + optional: Optional { third: Some(3) }, + some_other_field: 1337, + }); + + let ron = ron::ser::to_string_pretty( + &val, + ron::ser::PrettyConfig::default() + .extensions(ron::extensions::Extensions::UNWRAP_VARIANT_NEWTYPES), + ) + .unwrap(); + + assert_eq!( + ron, + "#![enable(unwrap_variant_newtypes)] +Main({ + \"first\": 1, + \"second\": 2, + \"third\": Some(3), + \"some_other_field\": 1337, +})" + ); + + let ron = ron::ser::to_string_pretty(&val, ron::ser::PrettyConfig::default()).unwrap(); + + let de: Newtype = ron::from_str(&ron).unwrap(); + + assert_eq!(de, val); + + assert_eq!( + ron::from_str::
( + "{ + first\": 1, + \"second\": 2, + \"third\": Some(3), + \"some_other_field\": 1337, + }" + ), + Err(ron::error::SpannedError { + code: ron::error::Error::ExpectedString, + position: ron::error::Position { line: 2, col: 9 }, + }) + ); + + assert_eq!( + ron::from_str::
( + "{ + \"first\": 1, + \"second: 2, + \"third\": Some(3), + \"some_other_field\": 1337, + }" + ), + Err(ron::error::SpannedError { + code: ron::error::Error::ExpectedMapColon, + position: ron::error::Position { line: 4, col: 10 }, + }) + ); + + assert_eq!( + ron::from_str::
( + "{ + \"first\": 1, + \"second\": 2, + third\": Some(3), + \"some_other_field\": 1337, + }" + ), + Err(ron::error::SpannedError { + code: ron::error::Error::ExpectedString, + position: ron::error::Position { line: 4, col: 9 }, + }) + ); + + assert_eq!( + ron::from_str::
( + "{ + \"first\": 1, + \"second\": 2, + \"third\": Some(3), + \"some_other_field: 1337, + }" + ), + Err(ron::error::SpannedError { + code: ron::error::Error::ExpectedStringEnd, + position: ron::error::Position { line: 5, col: 10 }, + }) + ); +} + +#[test] +fn test_flatten_rest() { + let val = MyType { + first: 1, + second: 2, + everything_else: { + let mut map = HashMap::new(); + map.insert( + String::from("third"), + ron::Value::Number(ron::value::Number::U8(3)), + ); + map + }, + }; + + let ron = ron::ser::to_string_pretty(&val, ron::ser::PrettyConfig::default()).unwrap(); + + assert_eq!( + ron, + "{ + \"first\": 1, + \"second\": 2, + \"third\": 3, +}" + ); + + let de: MyType = ron::from_str(&ron).unwrap(); + + assert_eq!(de, val); + + let val = Newtype::MyType(MyType { + first: 1, + second: 2, + everything_else: { + let mut map = HashMap::new(); + map.insert( + String::from("third"), + ron::Value::Number(ron::value::Number::U8(3)), + ); + map + }, + }); + + let ron = ron::ser::to_string_pretty( + &val, + ron::ser::PrettyConfig::default() + .extensions(ron::extensions::Extensions::UNWRAP_VARIANT_NEWTYPES), + ) + .unwrap(); + + assert_eq!( + ron, + "#![enable(unwrap_variant_newtypes)] +MyType({ + \"first\": 1, + \"second\": 2, + \"third\": 3, +})" + ); + + let de: Newtype = ron::from_str(&ron).unwrap(); + + assert_eq!(de, val); + + assert_eq!( + ron::from_str::( + "{ + first\": 1, + \"second\": 2, + \"third\": 3, + }" + ), + Err(ron::error::SpannedError { + code: ron::error::Error::ExpectedString, + position: ron::error::Position { line: 2, col: 9 }, + }) + ); + + assert_eq!( + ron::from_str::( + "{ + \"first\": 1, + \"second: 2, + \"third\": 3, + }" + ), + Err(ron::error::SpannedError { + code: ron::error::Error::ExpectedMapColon, + position: ron::error::Position { line: 4, col: 10 }, + }) + ); + + assert_eq!( + ron::from_str::( + "{ + \"first\": 1, + \"second\": 2, + third\": 3, + }" + ), + Err(ron::error::SpannedError { + code: ron::error::Error::ExpectedString, + position: ron::error::Position { line: 4, col: 9 }, + }) + ); + + assert_eq!( + ron::from_str::( + "{ + \"first\": 1, + \"second\": 2, + \"third: 3, + }" + ), + Err(ron::error::SpannedError { + code: ron::error::Error::ExpectedStringEnd, + position: ron::error::Position { line: 4, col: 10 }, + }) + ); +} + +#[test] +fn test_flatten_only_rest() { + let val = AllOptional { + everything_else: HashMap::new(), + }; + + let ron = ron::ser::to_string(&val).unwrap(); + + assert_eq!(ron, "{}"); + + let de: AllOptional = ron::from_str(&ron).unwrap(); + + assert_eq!(de, val); + + let val = Newtype::AllOptional(AllOptional { + everything_else: HashMap::new(), + }); + + let ron = ron::ser::to_string_pretty( + &val, + ron::ser::PrettyConfig::default() + .extensions(ron::extensions::Extensions::UNWRAP_VARIANT_NEWTYPES), + ) + .unwrap(); + + assert_eq!( + ron, + "#![enable(unwrap_variant_newtypes)] +AllOptional({ +})" + ); + + let de: Newtype = ron::from_str(&ron).unwrap(); + + assert_eq!(de, val); +} + +#[derive(Clone, Debug, Default, Deserialize, PartialEq, Eq, Serialize)] +#[serde(deny_unknown_fields)] +pub struct AvailableCards { + pub left: u8, + pub right: u8, +} + +#[derive(Clone, Debug, Deserialize, Serialize, PartialEq, Eq)] +struct MapProperties { + #[serde(flatten)] + cards: AvailableCards, +} + +#[test] +fn test_issue_456() { + let map_properties = MapProperties { + cards: AvailableCards { + ..Default::default() + }, + }; + let ron = ron::to_string(&map_properties).unwrap(); + + let de: MapProperties = ron::from_str(&ron).unwrap(); + + assert_eq!(map_properties, de); +} diff --git a/third_party/rust/ron/tests/117_untagged_tuple_variant.rs b/third_party/rust/ron/tests/117_untagged_tuple_variant.rs index 6d98deb65c98..0894694bd4de 100644 --- a/third_party/rust/ron/tests/117_untagged_tuple_variant.rs +++ b/third_party/rust/ron/tests/117_untagged_tuple_variant.rs @@ -23,7 +23,7 @@ fn test_ebkalderon_case() { flags: [ "--enable-thing", "--enable-other-thing", - If("some-conditional", ["--enable-third-thing"]), + ("some-conditional", ["--enable-third-thing"]), ] ) "#; diff --git a/third_party/rust/ron/tests/123_enum_representation.rs b/third_party/rust/ron/tests/123_enum_representation.rs index 414350f6942e..b00d94971c09 100644 --- a/third_party/rust/ron/tests/123_enum_representation.rs +++ b/third_party/rust/ron/tests/123_enum_representation.rs @@ -3,40 +3,50 @@ use std::{cmp::PartialEq, fmt::Debug}; use ron::{de::from_str, ser::to_string}; use serde::{Deserialize, Serialize}; -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Serialize, Deserialize)] +struct Unit; + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +struct Tuple(u8, u8); + +#[derive(Debug, PartialEq, Serialize, Deserialize)] enum Inner { Foo, Bar, } -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Serialize, Deserialize)] enum EnumStructExternally { - VariantA { foo: u32, bar: u32, different: u32 }, - VariantB { foo: u32, bar: u32 }, + VariantA { + foo: u32, + bar: Unit, + #[serde(with = "ByteStr")] + baz: Vec, + different: Tuple, + }, + VariantB { + foo: u32, + bar: Unit, + #[serde(with = "ByteStr")] + baz: Vec, + }, } -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Serialize, Deserialize)] #[serde(tag = "type")] enum EnumStructInternally { VariantA { foo: u32, bar: u32, different: u32 }, VariantB { foo: u32, bar: u32 }, } -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Serialize, Deserialize)] #[serde(tag = "type", content = "content")] enum EnumStructAdjacently { - VariantA { - foo: u32, - bar: u32, - different: Inner, - }, - VariantB { - foo: u32, - bar: u32, - }, + VariantA { foo: f64, bar: (), different: Inner }, + VariantB { foo: f64, bar: () }, } -#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +#[derive(Debug, PartialEq, Serialize, Deserialize)] #[serde(untagged)] enum EnumStructUntagged { VariantA { foo: u32, bar: u32, different: u32 }, @@ -69,17 +79,22 @@ where fn test_externally_a_ser() { let v = EnumStructExternally::VariantA { foo: 1, - bar: 2, - different: 3, + bar: Unit, + baz: vec![b'a'], + different: Tuple(2, 3), }; - let e = "VariantA(foo:1,bar:2,different:3)"; + let e = "VariantA(foo:1,bar:(),baz:b\"a\",different:(2,3))"; test_ser(&v, e); } #[test] fn test_externally_b_ser() { - let v = EnumStructExternally::VariantB { foo: 1, bar: 2 }; - let e = "VariantB(foo:1,bar:2)"; + let v = EnumStructExternally::VariantB { + foo: 1, + bar: Unit, + baz: vec![b'a'], + }; + let e = "VariantB(foo:1,bar:(),baz:b\"a\")"; test_ser(&v, e); } @@ -104,18 +119,18 @@ fn test_internally_b_ser() { #[test] fn test_adjacently_a_ser() { let v = EnumStructAdjacently::VariantA { - foo: 1, - bar: 2, + foo: 1.0, + bar: (), different: Inner::Foo, }; - let e = "(type:VariantA,content:(foo:1,bar:2,different:Foo))"; + let e = "(type:VariantA,content:(foo:1.0,bar:(),different:Foo))"; test_ser(&v, e); } #[test] fn test_adjacently_b_ser() { - let v = EnumStructAdjacently::VariantB { foo: 1, bar: 2 }; - let e = "(type:VariantB,content:(foo:1,bar:2))"; + let v = EnumStructAdjacently::VariantB { foo: 1.0, bar: () }; + let e = "(type:VariantB,content:(foo:1.0,bar:()))"; test_ser(&v, e); } @@ -139,19 +154,24 @@ fn test_untagged_b_ser() { #[test] fn test_externally_a_de() { - let s = "VariantA(foo:1,bar:2,different:3)"; + let s = "VariantA(foo:1,bar:Unit,baz:b\"a\",different:Tuple(2,3))"; let e = EnumStructExternally::VariantA { foo: 1, - bar: 2, - different: 3, + bar: Unit, + baz: vec![b'a'], + different: Tuple(2, 3), }; test_de(s, e); } #[test] fn test_externally_b_de() { - let s = "VariantB(foo:1,bar:2)"; - let e = EnumStructExternally::VariantB { foo: 1, bar: 2 }; + let s = "VariantB(foo:1,bar:Unit,baz:b\"a\")"; + let e = EnumStructExternally::VariantB { + foo: 1, + bar: Unit, + baz: vec![b'a'], + }; test_de(s, e); } @@ -175,10 +195,10 @@ fn test_internally_b_de() { #[test] fn test_adjacently_a_de() { - let s = "(type:VariantA,content:(foo:1,bar:2,different:Foo))"; + let s = "(type:VariantA,content:(foo:1.0,bar:(),different:Foo))"; let e = EnumStructAdjacently::VariantA { - foo: 1, - bar: 2, + foo: 1.0, + bar: (), different: Inner::Foo, }; test_de(s, e); @@ -186,8 +206,8 @@ fn test_adjacently_a_de() { #[test] fn test_adjacently_b_de() { - let s = "(type:VariantB,content:(foo:1,bar:2))"; - let e = EnumStructAdjacently::VariantB { foo: 1, bar: 2 }; + let s = "(type:VariantB,content:(foo:1.0,bar:()))"; + let e = EnumStructAdjacently::VariantB { foo: 1.0, bar: () }; test_de(s, e); } @@ -213,15 +233,20 @@ fn test_untagged_b_de() { fn test_externally_a_roundtrip() { let v = EnumStructExternally::VariantA { foo: 1, - bar: 2, - different: 3, + bar: Unit, + baz: vec![b'a'], + different: Tuple(2, 3), }; test_roundtrip(v); } #[test] fn test_externally_b_roundtrip() { - let v = EnumStructExternally::VariantB { foo: 1, bar: 2 }; + let v = EnumStructExternally::VariantB { + foo: 1, + bar: Unit, + baz: vec![b'a'], + }; test_roundtrip(v); } @@ -244,8 +269,8 @@ fn test_internally_b_roundtrip() { #[test] fn test_adjacently_a_roundtrip() { let v = EnumStructAdjacently::VariantA { - foo: 1, - bar: 2, + foo: 1.0, + bar: (), different: Inner::Foo, }; test_roundtrip(v); @@ -253,7 +278,7 @@ fn test_adjacently_a_roundtrip() { #[test] fn test_adjacently_b_roundtrip() { - let v = EnumStructAdjacently::VariantB { foo: 1, bar: 2 }; + let v = EnumStructAdjacently::VariantB { foo: 1.0, bar: () }; test_roundtrip(v); } @@ -272,3 +297,31 @@ fn test_untagged_b_roundtrip() { let v = EnumStructUntagged::VariantB { foo: 1, bar: 2 }; test_roundtrip(v); } + +enum ByteStr {} + +impl ByteStr { + fn serialize(data: &[u8], serializer: S) -> Result { + serializer.serialize_bytes(data) + } + + fn deserialize<'de, D: serde::Deserializer<'de>>(deserializer: D) -> Result, D::Error> { + struct ByteStrVisitor; + + impl<'de> serde::de::Visitor<'de> for ByteStrVisitor { + type Value = Vec; + + // GRCOV_EXCL_START + fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.write_str("a Rusty byte string") + } + // GRCOV_EXCL_STOP + + fn visit_bytes(self, bytes: &[u8]) -> Result { + Ok(bytes.to_vec()) + } + } + + deserializer.deserialize_bytes(ByteStrVisitor) + } +} diff --git a/third_party/rust/ron/tests/147_empty_sets_serialisation.rs b/third_party/rust/ron/tests/147_empty_sets_serialisation.rs index d93f9dd118f9..0595aa0ba84c 100644 --- a/third_party/rust/ron/tests/147_empty_sets_serialisation.rs +++ b/third_party/rust/ron/tests/147_empty_sets_serialisation.rs @@ -37,7 +37,7 @@ fn empty_sets_arrays() { let pretty = ron::ser::PrettyConfig::new() .enumerate_arrays(true) - .new_line("\n".to_string()); + .new_line("\n"); let serial = ron::ser::to_string_pretty(&value, pretty).unwrap(); println!("Serialized: {}", serial); diff --git a/third_party/rust/ron/tests/152_bitflags.rs b/third_party/rust/ron/tests/152_bitflags.rs index b4085b93d2bb..53b0cb068c0d 100644 --- a/third_party/rust/ron/tests/152_bitflags.rs +++ b/third_party/rust/ron/tests/152_bitflags.rs @@ -1,6 +1,7 @@ use bitflags::bitflags; use option_set::option_set; +// GRCOV_EXCL_START bitflags! { #[derive( Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, @@ -12,7 +13,9 @@ bitflags! { const THREE = 1 << 2; } } +// GRCOV_EXCL_STOP +// GRCOV_EXCL_START option_set! { struct TestBad: UpperCamel + u8 { const ONE = 1; @@ -20,6 +23,7 @@ option_set! { const THREE = 1 << 2; } } +// GRCOV_EXCL_STOP #[test] fn test_bitflags() { diff --git a/third_party/rust/ron/tests/217_nested_untagged_enums.rs b/third_party/rust/ron/tests/217_nested_untagged_enums.rs new file mode 100644 index 000000000000..32db343741a4 --- /dev/null +++ b/third_party/rust/ron/tests/217_nested_untagged_enums.rs @@ -0,0 +1,98 @@ +use serde::{Deserialize, Serialize}; + +#[test] +fn nested_untagged_enum() { + // Contributed by @caelunshun in https://github.com/ron-rs/ron/issues/217 + + #[derive(Debug, PartialEq, Deserialize)] + struct Root { + value: Either, + } + + #[derive(Debug, PartialEq, Deserialize)] + #[serde(transparent)] + struct Newtype(Either); + + #[derive(Debug, PartialEq, Deserialize)] + #[serde(untagged)] + enum Either { + One(One), + Two(Two), + } + + #[derive(Debug, PartialEq, Deserialize)] + enum One { + OneA, + OneB, + OneC, + } + + #[derive(Debug, PartialEq, Deserialize)] + enum Two { + TwoA, + TwoB, + TwoC, + } + + assert_eq!(ron::de::from_str("OneA"), Ok(One::OneA)); + assert_eq!(ron::de::from_str("OneA"), Ok(Either::One(One::OneA))); + assert_eq!( + ron::de::from_str("(value: OneA)"), + Ok(Root { + value: Either::One(One::OneA) + }) + ); + assert_eq!( + ron::de::from_str("OneA"), + Ok(Newtype(Either::One(One::OneA))) + ); +} + +#[test] +fn untagged_enum_of_enum_list() { + // Contributed by @joonazan in https://github.com/ron-rs/ron/issues/217 + + #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] + pub enum UnitType { + Explorer, + } + + #[derive(Serialize, Deserialize, Debug, PartialEq)] + #[serde(untagged)] + enum CardTextNumberFlat { + JustNum(u8), + RangeW(Range_), + CountUnitW(CountUnit_), + PowerCardsPlayedW(PowerCardsPlayed), + } + + #[allow(non_snake_case)] + #[derive(Serialize, Deserialize, Debug, PartialEq)] + struct Range_ { + Range: u8, + } + #[derive(Serialize, Deserialize, Debug, PartialEq)] + #[allow(non_snake_case)] + struct CountUnit_ { + CountUnit: Vec, + } + #[derive(Serialize, Deserialize, Debug, PartialEq)] + struct PowerCardsPlayed; + + let units = CardTextNumberFlat::CountUnitW(CountUnit_ { + CountUnit: vec![UnitType::Explorer], + }); + let range = CardTextNumberFlat::RangeW(Range_ { Range: 1 }); + + let units_ron: String = ron::to_string(&units).unwrap(); + let range_ron = ron::to_string(&range).unwrap(); + + assert_eq!(units_ron, "(CountUnit:[Explorer])"); + assert_eq!(range_ron, "(Range:1)"); + + let units_de: CardTextNumberFlat = ron::from_str(&units_ron).unwrap(); + let range_de: CardTextNumberFlat = ron::from_str(&range_ron).unwrap(); + + assert_eq!(units_de, units); + assert_eq!(range_de, range); +} diff --git a/third_party/rust/ron/tests/238_array.rs b/third_party/rust/ron/tests/238_array.rs index 8429f804cee9..ec339b271212 100644 --- a/third_party/rust/ron/tests/238_array.rs +++ b/third_party/rust/ron/tests/238_array.rs @@ -17,9 +17,9 @@ fn test_array() { assert_eq!( value, Value::Seq(vec![ - Value::Number(Number::from(1)), - Value::Number(Number::from(2)), - Value::Number(Number::from(3)), + Value::Number(Number::U8(1)), + Value::Number(Number::U8(2)), + Value::Number(Number::U8(3)), ]) ); @@ -43,9 +43,9 @@ fn test_array() { assert_eq!( value, Value::Seq(vec![ - Value::Number(Number::from(1)), - Value::Number(Number::from(2)), - Value::Number(Number::from(3)), + Value::Number(Number::U8(1)), + Value::Number(Number::U8(2)), + Value::Number(Number::U8(3)), ]) ); } diff --git a/third_party/rust/ron/tests/240_array_pretty.rs b/third_party/rust/ron/tests/240_array_pretty.rs index db49f5767731..ffc3460a718e 100644 --- a/third_party/rust/ron/tests/240_array_pretty.rs +++ b/third_party/rust/ron/tests/240_array_pretty.rs @@ -4,7 +4,7 @@ use ron::ser::{to_string_pretty, PrettyConfig}; fn small_array() { let arr = &[(), (), ()][..]; assert_eq!( - to_string_pretty(&arr, PrettyConfig::new().new_line("\n".to_string())).unwrap(), + to_string_pretty(&arr, PrettyConfig::new().new_line("\n")).unwrap(), "[ (), (), @@ -14,9 +14,7 @@ fn small_array() { assert_eq!( to_string_pretty( &arr, - PrettyConfig::new() - .new_line("\n".to_string()) - .compact_arrays(true) + PrettyConfig::new().new_line("\n").compact_arrays(true) ) .unwrap(), "[(), (), ()]" @@ -25,9 +23,9 @@ fn small_array() { to_string_pretty( &arr, PrettyConfig::new() - .new_line("\n".to_string()) + .new_line("\n") .compact_arrays(true) - .separator("".to_string()) + .separator("") ) .unwrap(), "[(),(),()]" @@ -36,7 +34,7 @@ fn small_array() { to_string_pretty( &vec![(1, 2), (3, 4)], PrettyConfig::new() - .new_line("\n".to_string()) + .new_line("\n") .separate_tuple_members(true) .compact_arrays(true) ) diff --git a/third_party/rust/ron/tests/250_variant_newtypes.rs b/third_party/rust/ron/tests/250_variant_newtypes.rs index 55673e92b974..6b59625831b8 100644 --- a/third_party/rust/ron/tests/250_variant_newtypes.rs +++ b/third_party/rust/ron/tests/250_variant_newtypes.rs @@ -286,6 +286,30 @@ fn test_deserialise_tuple_newtypes() { ); } +#[test] +fn test_newtype_some() { + assert_eq!( + from_str::>(r#"Some((a: 4, b: false))"#).unwrap(), + Some(Struct { a: 4, b: false }), + ); + + assert_eq!( + from_str::>(r#"Some(a: 4, b: false)"#) + .unwrap_err() + .code, + Error::ExpectedDifferentStructName { + expected: "Struct", + found: String::from("a"), + }, + ); + + assert_eq!( + from_str::>(r#"#![enable(unwrap_variant_newtypes)] Some(a: 4, b: false)"#) + .unwrap(), + Some(Struct { a: 4, b: false }), + ); +} + #[test] fn test_serialise_non_newtypes() { assert_eq_serialize_roundtrip(TestEnum::Unit, Extensions::UNWRAP_VARIANT_NEWTYPES); @@ -400,11 +424,8 @@ fn assert_eq_serialize_roundtrip< value: S, extensions: Extensions, ) { - assert_eq!( - from_str::( - &to_string_pretty(&value, PrettyConfig::default().extensions(extensions)).unwrap() - ) - .unwrap(), - value, - ); + let ron = to_string_pretty(&value, PrettyConfig::default().extensions(extensions)).unwrap(); + let result = from_str::(&ron); + + assert_eq!(result.as_ref(), Ok(&value), "{}", ron,); } diff --git a/third_party/rust/ron/tests/254_typetag.rs b/third_party/rust/ron/tests/254_typetag.rs new file mode 100644 index 000000000000..96ac83274042 --- /dev/null +++ b/third_party/rust/ron/tests/254_typetag.rs @@ -0,0 +1,188 @@ +use std::fmt::Write; + +use serde::{Deserialize, Serialize}; + +#[test] +fn typetag_usage() { + #[derive(Deserialize, Serialize, Debug)] + struct Component1 { + value: f32, + value_2: f32, + } + + #[derive(Deserialize, Serialize, Debug)] + struct Component2 { + value: f32, + } + + #[typetag::serde(tag = "type")] + trait MyTrait: std::fmt::Debug { + fn do_stuff(&self, buffer: &mut String); + } + + #[typetag::serde] + impl MyTrait for Component1 { + fn do_stuff(&self, buffer: &mut String) { + let _ = writeln!(buffer, "{:#?}", self); + } + } + + #[typetag::serde] + impl MyTrait for Component2 { + fn do_stuff(&self, buffer: &mut String) { + let _ = writeln!(buffer, "{:#?}", self); + } + } + + #[derive(Deserialize, Serialize, Debug)] + struct EntityConfig { + name: String, + components: Vec>, + } + + let ron_config = r#"EntityConfig( + name: "some name", + components: [ + { + "type": "Component1", + "value": 22.0, + "value_2": 35.0, + }, + { + "type": "Component2", + "value": 3.14, + }, + ], +)"#; + + let entity_config: EntityConfig = ron::from_str(ron_config).unwrap(); + + assert_eq!(entity_config.name, "some name"); + assert_eq!(entity_config.components.len(), 2); + + let mut buffer = String::new(); + + for component in &entity_config.components { + component.do_stuff(&mut buffer); + } + + assert_eq!( + buffer, + r#"Component1 { + value: 22.0, + value_2: 35.0, +} +Component2 { + value: 3.14, +} +"# + ); + + let ron = ron::ser::to_string_pretty( + &entity_config, + ron::ser::PrettyConfig::default().struct_names(true), + ) + .unwrap(); + assert_eq!(ron, ron_config); +} + +#[test] +fn typetag_with_enum() { + #[derive(Deserialize, Serialize, Debug)] + enum SampleTestEnum { + Test(i32), + AnotherTest(f32, f32), + } + + #[derive(Deserialize, Serialize, Debug)] + struct Component1 { + value: f32, + value_2: f32, + something: SampleTestEnum, + } + + #[derive(Deserialize, Serialize, Debug)] + struct Component2 { + value: f32, + } + + #[typetag::serde(tag = "type")] + trait MyTrait: std::fmt::Debug { + fn do_stuff(&self, buffer: &mut String); + } + + #[typetag::serde] + impl MyTrait for Component1 { + fn do_stuff(&self, buffer: &mut String) { + match self.something { + SampleTestEnum::Test(number) => { + let _ = writeln!(buffer, "my number: {:#?}", number); + } + SampleTestEnum::AnotherTest(float_1, float_2) => { + let _ = writeln!(buffer, "f1: {:#?}, f2: {:#?}", float_1, float_2); + } + } + } + } + + #[typetag::serde] + impl MyTrait for Component2 { + fn do_stuff(&self, buffer: &mut String) { + let _ = writeln!(buffer, "{:#?}", self.value); + } + } + + #[derive(Deserialize, Serialize, Debug)] + struct EntityConfig { + name: String, + components: Vec>, + } + + let ron_config = r#"EntityConfig( + name: "some other name", + components: [ + { + "type": "Component1", + "value": 22.0, + "value_2": 35.0, + "something": Test(22), + }, + { + "type": "Component1", + "value": 12.0, + "value_2": 11.0, + "something": AnotherTest(11.0, 22.0), + }, + { + "type": "Component2", + "value": 3.1, + }, + ], +)"#; + + let entity_config: EntityConfig = ron::from_str(ron_config).unwrap(); + + assert_eq!(entity_config.name, "some other name"); + assert_eq!(entity_config.components.len(), 3); + + let mut buffer = String::new(); + + for component in &entity_config.components { + component.do_stuff(&mut buffer); + } + + assert_eq!( + buffer, + r#"my number: 22 +f1: 11.0, f2: 22.0 +3.1 +"# + ); + + let ron = ron::ser::to_string_pretty( + &entity_config, + ron::ser::PrettyConfig::default().struct_names(true), + ) + .unwrap(); + assert_eq!(ron, ron_config); +} diff --git a/third_party/rust/ron/tests/256_comma_error.rs b/third_party/rust/ron/tests/256_comma_error.rs index 4a873e476256..0e0c4cd25532 100644 --- a/third_party/rust/ron/tests/256_comma_error.rs +++ b/third_party/rust/ron/tests/256_comma_error.rs @@ -62,6 +62,19 @@ fn test_missing_comma_error() { position: Position { line: 3, col: 9 } } ); + + let extensions_string = r#"#![enable( + implicit_some // <-- forgotten comma here + unwrap_newtypes + ]) 42"#; + + assert_eq!( + ron::from_str::(extensions_string).unwrap_err(), + SpannedError { + code: Error::ExpectedComma, + position: Position { line: 3, col: 9 } + } + ); } #[test] diff --git a/third_party/rust/ron/tests/321_unicode_ident.rs b/third_party/rust/ron/tests/321_unicode_ident.rs new file mode 100644 index 000000000000..21ad77af9145 --- /dev/null +++ b/third_party/rust/ron/tests/321_unicode_ident.rs @@ -0,0 +1,30 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Deserialize, PartialEq, Serialize)] +enum EnumWithUnicode { + Äöß, + 你好世界, +} + +#[test] +fn roundtrip_unicode_ident() { + let value = [EnumWithUnicode::Äöß, EnumWithUnicode::你好世界]; + + let ron = ron::ser::to_string(&value).unwrap(); + assert_eq!(ron, "(Äöß,你好世界)"); + + let de = ron::de::from_str(&ron); + assert_eq!(Ok(value), de); +} + +#[test] +fn fuzzer_issues() { + assert_eq!( + ron::from_str::("(__: ())").unwrap(), + ron::Value::Map( + [(ron::Value::String(String::from("__")), ron::Value::Unit)] + .into_iter() + .collect() + ) + ); +} diff --git a/third_party/rust/ron/tests/337_value_float_roundtrip.rs b/third_party/rust/ron/tests/337_value_float_roundtrip.rs index 66873eaeab49..a5add46472e6 100644 --- a/third_party/rust/ron/tests/337_value_float_roundtrip.rs +++ b/third_party/rust/ron/tests/337_value_float_roundtrip.rs @@ -2,7 +2,7 @@ fn roundtrip_value_float_with_decimals() { let v: ron::Value = ron::from_str("1.0").unwrap(); - assert_eq!(v, ron::Value::Number(1.0_f64.into())); + assert_eq!(v, ron::Value::Number(1.0_f32.into())); let ser = ron::ser::to_string(&v).unwrap(); @@ -15,7 +15,7 @@ fn roundtrip_value_float_with_decimals() { #[allow(clippy::float_cmp)] fn roundtrip_value_float_into() { let v: ron::Value = ron::from_str("1.0").unwrap(); - assert_eq!(v, ron::Value::Number(1.0_f64.into())); + assert_eq!(v, ron::Value::Number(1.0_f32.into())); let ser = ron::ser::to_string(&v).unwrap(); diff --git a/third_party/rust/ron/tests/357_untagged_enum_roundtrip.rs b/third_party/rust/ron/tests/357_untagged_enum_roundtrip.rs new file mode 100644 index 000000000000..340a4d494457 --- /dev/null +++ b/third_party/rust/ron/tests/357_untagged_enum_roundtrip.rs @@ -0,0 +1,33 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +#[serde(untagged)] +enum MyValue { + Int(i64), + String(String), + Enum(Enum), + List(Vec), +} + +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +enum Enum { + First(String), + Second(i64), +} + +#[test] +fn untagged_enum_not_a_list() { + // Contributed by @obi1kenobi in https://github.com/ron-rs/ron/issues/357 + + let value = MyValue::Enum(Enum::First("foo".to_string())); + + let ron = ron::to_string(&value).unwrap(); + assert_eq!(ron, "First(\"foo\")"); + + let de = ron::from_str(&ron).unwrap(); + + println!("{}", ron); + + // This used to fail as the value was deserialised as `List([String("foo")])` + assert_eq!(value, de); +} diff --git a/third_party/rust/ron/tests/367_implicit_some.rs b/third_party/rust/ron/tests/367_implicit_some.rs index 4eeac4df35fe..c65d8efc1bb8 100644 --- a/third_party/rust/ron/tests/367_implicit_some.rs +++ b/third_party/rust/ron/tests/367_implicit_some.rs @@ -106,3 +106,39 @@ fn test_nested_implicit_some() { Ok(Some(Some(Some(5)))) ); } + +#[test] +fn fuzzer_found_issues() { + #[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)] + struct Some_(); + + #[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)] + struct None_(); + + let ser_some = ron::ser::to_string_pretty( + &Some(Some_()), + ron::ser::PrettyConfig::default() + .struct_names(true) + .extensions(ron::extensions::Extensions::IMPLICIT_SOME), + ) + .unwrap(); + assert_eq!(ser_some, "#![enable(implicit_some)]\nSome_()"); + + let ser_none = ron::ser::to_string_pretty( + &Some(None_()), + ron::ser::PrettyConfig::default() + .struct_names(true) + .extensions(ron::extensions::Extensions::IMPLICIT_SOME), + ) + .unwrap(); + assert_eq!(ser_none, "#![enable(implicit_some)]\nNone_()"); + + assert_eq!( + ron::from_str::>(&ser_some).unwrap(), + Some(Some_()) + ); + assert_eq!( + ron::from_str::>(&ser_none).unwrap(), + Some(None_()) + ); +} diff --git a/third_party/rust/ron/tests/370_float_parsing.rs b/third_party/rust/ron/tests/370_float_parsing.rs index 786d5ae331eb..a620fc98c97e 100644 --- a/third_party/rust/ron/tests/370_float_parsing.rs +++ b/third_party/rust/ron/tests/370_float_parsing.rs @@ -34,33 +34,15 @@ fn test_float_literal_parsing() { assert_eq!(ron::from_str(".3e+1"), Ok(3.0_f64)); assert_eq!(ron::from_str(".4E-1"), Ok(0.04_f64)); - assert_eq!( - ron::from_str::("1_0.1_0"), - Err(SpannedError { - code: Error::FloatUnderscore, - position: Position { line: 1, col: 2 }, - }) - ); - assert_eq!( - ron::from_str::("1_0.10"), - Err(SpannedError { - code: Error::FloatUnderscore, - position: Position { line: 1, col: 2 }, - }) - ); - assert_eq!( - ron::from_str::("10.1_0"), - Err(SpannedError { - code: Error::FloatUnderscore, - position: Position { line: 1, col: 5 }, - }) - ); + assert_eq!(ron::from_str("1_0.1_0"), Ok(10.1_f32),); + assert_eq!(ron::from_str("1_0.10"), Ok(10.1_f32),); + assert_eq!(ron::from_str("10.1_0"), Ok(10.1_f32),); assert_eq!( ron::from_str::("1.0e1.0"), Err(SpannedError { code: Error::ExpectedFloat, - position: Position { line: 1, col: 8 }, + position: Position { line: 1, col: 1 }, }) ); } diff --git a/third_party/rust/ron/tests/401_raw_identifier.rs b/third_party/rust/ron/tests/401_raw_identifier.rs index 23e6ecabd2eb..740ad6211fdb 100644 --- a/third_party/rust/ron/tests/401_raw_identifier.rs +++ b/third_party/rust/ron/tests/401_raw_identifier.rs @@ -15,6 +15,7 @@ struct EmptyStruct; struct RawStruct { #[serde(rename = "ab.cd-ef")] field: bool, + really_not_raw: i32, } #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] @@ -108,6 +109,20 @@ fn test_invalid_identifiers() { } ); + let de = ron::from_str::( + "r#Hello+World( + rab.cd-ef: true, + )", + ) + .unwrap_err(); + assert_eq!( + de, + SpannedError { + code: Error::SuggestRawIdentifier(String::from("rab.cd-ef")), + position: Position { line: 2, col: 9 }, + } + ); + let de = ron::from_str::( "r#Hello+World( r#ab.cd+ef: true, @@ -118,7 +133,7 @@ fn test_invalid_identifiers() { de, SpannedError { code: Error::NoSuchStructField { - expected: &["ab.cd-ef"], + expected: &["ab.cd-ef", "really_not_raw"], found: String::from("ab.cd+ef"), outer: Some(String::from("Hello+World")), }, @@ -151,18 +166,24 @@ fn test_invalid_identifiers() { let de = ron::from_str::("r#+").unwrap_err(); assert_eq!( format!("{}", de), - r#"1:4: Expected struct ""_[invalid identifier] but found `r#+`"#, + r#"1:4: Expected only opening `(`, no name, for un-nameable struct"#, ); } #[test] fn test_raw_identifier_roundtrip() { - let val = RawStruct { field: true }; + let val = RawStruct { + field: true, + really_not_raw: 42, + }; let ser = ron::ser::to_string_pretty(&val, ron::ser::PrettyConfig::default().struct_names(true)) .unwrap(); - assert_eq!(ser, "r#Hello+World(\n r#ab.cd-ef: true,\n)"); + assert_eq!( + ser, + "r#Hello+World(\n r#ab.cd-ef: true,\n really_not_raw: 42,\n)" + ); let de: RawStruct = ron::from_str(&ser).unwrap(); assert_eq!(de, val); diff --git a/third_party/rust/ron/tests/407_raw_value.rs b/third_party/rust/ron/tests/407_raw_value.rs new file mode 100644 index 000000000000..f6a0cdea4c73 --- /dev/null +++ b/third_party/rust/ron/tests/407_raw_value.rs @@ -0,0 +1,374 @@ +use ron::{ + de::from_bytes, + error::{Error, Position, SpannedError}, + from_str, to_string, + value::RawValue, +}; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] +struct WithRawValue { + a: bool, + b: Box, +} + +#[test] +fn test_raw_value_simple() { + let raw: &RawValue = from_str("true").unwrap(); + assert_eq!(raw.get_ron(), "true"); + let ser = to_string(raw).unwrap(); + assert_eq!(ser, "true"); +} + +#[test] +fn test_raw_value_inner() { + let raw: WithRawValue = from_str("(a: false, b: [1, /* lol */ 2, 3])").unwrap(); + assert_eq!(raw.b.get_ron(), " [1, /* lol */ 2, 3]"); + let ser = to_string(&raw).unwrap(); + assert_eq!(ser, "(a:false,b: [1, /* lol */ 2, 3])"); +} + +#[test] +fn test_raw_value_comment() { + let raw: WithRawValue = from_str("(a: false, b: /* yeah */ 4)").unwrap(); + assert_eq!(raw.b.get_ron(), " /* yeah */ 4"); + + let raw: WithRawValue = from_str("(a: false, b: 4 /* yes */)").unwrap(); + assert_eq!(raw.b.get_ron(), " 4 /* yes */"); + + let raw: WithRawValue = from_str("(a: false, b: (/* this */ 4 /* too */))").unwrap(); + assert_eq!(raw.b.get_ron(), " (/* this */ 4 /* too */)"); +} + +#[test] +fn test_raw_value_invalid() { + let err = from_str::<&RawValue>("4.d").unwrap_err(); + assert_eq!( + err, + SpannedError { + code: Error::TrailingCharacters, + position: Position { line: 1, col: 3 } + } + ); + + let err = from_bytes::<&RawValue>(b"\0").unwrap_err(); + assert_eq!( + err, + SpannedError { + code: Error::UnexpectedChar('\0'), + position: Position { line: 1, col: 1 } + } + ) +} + +#[test] +fn test_raw_value_from_ron() { + let raw = RawValue::from_ron("/* hi */ (None, 4.2) /* bye */").unwrap(); + assert_eq!(raw.get_ron(), "/* hi */ (None, 4.2) /* bye */"); + + let err = RawValue::from_ron("4.d").unwrap_err(); + assert_eq!( + err, + SpannedError { + code: Error::TrailingCharacters, + position: Position { line: 1, col: 3 } + } + ); + + let raw = + RawValue::from_boxed_ron(String::from("/* hi */ (None, 4.2) /* bye */").into_boxed_str()) + .unwrap(); + assert_eq!(raw.get_ron(), "/* hi */ (None, 4.2) /* bye */"); + + let err = RawValue::from_boxed_ron(String::from("(").into_boxed_str()).unwrap_err(); + assert_eq!( + err, + SpannedError { + code: Error::Eof, + position: Position { line: 1, col: 2 }, + } + ); +} + +#[test] +fn test_raw_value_into_rust() { + let raw = RawValue::from_ron("/* hi */ (a: false, b: None) /* bye */").unwrap(); + + let with: WithRawValue = raw.into_rust().unwrap(); + assert_eq!( + with, + WithRawValue { + a: false, + b: from_str(" None").unwrap(), + } + ); + + let err = raw.into_rust::().unwrap_err(); + assert_eq!( + err, + SpannedError { + code: Error::ExpectedInteger, + position: Position { line: 1, col: 10 }, + } + ); +} + +#[test] +fn test_raw_value_from_rust() { + let raw = RawValue::from_rust(&42).unwrap(); + assert_eq!(raw.get_ron(), "42"); + + let raw = RawValue::from_rust(&WithRawValue { + a: true, + b: from_str("4.2").unwrap(), + }) + .unwrap(); + assert_eq!(raw.get_ron(), "(a:true,b:4.2)"); +} + +#[test] +fn test_raw_value_serde_json() { + let raw = RawValue::from_ron("/* hi */ (None, 4.2) /* bye */").unwrap(); + + let ser = serde_json::to_string(&WithRawValue { + a: true, + b: raw.to_owned(), + }) + .unwrap(); + assert_eq!(ser, "{\"a\":true,\"b\":\"/* hi */ (None, 4.2) /* bye */\"}"); + + let with: WithRawValue = serde_json::from_str(&ser).unwrap(); + assert_eq!(raw, &*with.b); + + let err = + serde_json::from_str::("{\"a\":true,\"b\":\"/* hi */ (a:) /* bye */\"}") + .unwrap_err(); + assert_eq!( + err.to_string(), + "invalid RON value at 1:13: Unexpected char ')' at line 1 column 39" + ); + + let err = serde_json::from_str::("{\"a\":true,\"b\":42}").unwrap_err(); + assert_eq!( + err.to_string(), + "invalid type: integer `42`, expected any valid RON-value-string at line 1 column 16" + ); + + let err = serde_json::from_str::<&RawValue>("\"/* hi */ (a:) /* bye */\"").unwrap_err(); + assert_eq!( + err.to_string(), + "invalid RON value at 1:13: Unexpected char ')' at line 1 column 25" + ); + + let err = serde_json::from_str::<&RawValue>("42").unwrap_err(); + assert_eq!( + err.to_string(), + "invalid type: integer `42`, expected any valid borrowed RON-value-string at line 1 column 2" + ); +} + +#[test] +fn test_raw_value_clone_into() { + let raw = RawValue::from_boxed_ron(String::from("(None, 4.2)").into_boxed_str()).unwrap(); + let raw2 = raw.clone(); + assert_eq!(raw, raw2); + + let boxed_str: Box = raw2.into(); + assert_eq!(&*boxed_str, "(None, 4.2)"); +} + +#[test] +fn test_raw_value_debug_display() { + let raw = RawValue::from_ron("/* hi */ (None, 4.2) /* bye */").unwrap(); + + assert_eq!(format!("{}", raw), "/* hi */ (None, 4.2) /* bye */"); + assert_eq!( + format!("{:#?}", raw), + "\ +RawValue( + /* hi */ (None, 4.2) /* bye */, +)\ + " + ); +} + +#[test] +fn test_boxed_raw_value_deserialise_from_string() { + let string = serde::de::value::StringDeserializer::::new(String::from("4.2")); + + let err = <&RawValue>::deserialize(string.clone()).unwrap_err(); + assert_eq!( + err, + Error::InvalidValueForType { + expected: String::from("any valid borrowed RON-value-string"), + found: String::from("the string \"4.2\""), + } + ); + + let boxed_raw = Box::::deserialize(string).unwrap(); + assert_eq!(boxed_raw.get_ron(), "4.2"); + + let string = serde::de::value::StringDeserializer::::new(String::from("[")); + + let err = Box::::deserialize(string).unwrap_err(); + assert_eq!( + err, + Error::Message(String::from( + "invalid RON value at 1:2: Unexpected end of RON" + )) + ); +} + +#[test] +fn test_whitespace_rountrip_issues_and_trim() { + let mut v = WithRawValue { + a: true, + b: RawValue::from_ron("42 ").unwrap().to_owned(), + }; + v = ron::from_str(&ron::to_string(&v).unwrap()).unwrap(); + assert_eq!(v.b.get_ron(), "42 "); + assert_eq!(v.b.trim().get_ron(), "42"); + assert_eq!(v.b.to_owned().trim_boxed().get_ron(), "42"); + + for i in 1..=10 { + v = ron::from_str( + &ron::ser::to_string_pretty(&v, ron::ser::PrettyConfig::default()).unwrap(), + ) + .unwrap(); + assert_eq!(v.b.get_ron(), &format!("{: <1$}42 ", "", i)); + assert_eq!(v.b.trim().get_ron(), "42"); + assert_eq!(v.b.to_owned().trim_boxed().get_ron(), "42"); + } + + assert_eq!(RawValue::from_ron("42").unwrap().trim().get_ron(), "42"); + assert_eq!( + RawValue::from_ron(" /* start */ 42 // end\n ") + .unwrap() + .trim() + .get_ron(), + "42" + ); + + assert_eq!( + RawValue::from_ron("42") + .unwrap() + .to_owned() + .trim_boxed() + .get_ron(), + "42" + ); + assert_eq!( + RawValue::from_ron(" /* start */ 42 // end\n ") + .unwrap() + .to_owned() + .trim_boxed() + .get_ron(), + "42" + ); +} + +#[test] +fn test_fuzzer_found_issue() { + assert_eq!( + RawValue::from_ron(""), + Err(SpannedError { + code: Error::Eof, + position: Position { line: 1, col: 1 }, + }) + ); + + assert_eq!(ron::from_str("C"), RawValue::from_ron("C")); + + assert_eq!(ron::from_str(" t "), RawValue::from_ron(" t ")); + + assert_eq!( + RawValue::from_ron("#![enable(implicit_some)] 42"), + Err(SpannedError { + code: Error::Message(String::from( + "ron::value::RawValue cannot enable extensions" + )), + position: Position { line: 1, col: 27 }, + }) + ); + + assert_eq!( + RawValue::from_boxed_ron(String::from("#![enable(implicit_some)] 42").into_boxed_str()), + Err(SpannedError { + code: Error::Message(String::from( + "ron::value::RawValue cannot enable extensions" + )), + position: Position { line: 1, col: 27 }, + }) + ); + + assert_eq!( + RawValue::from_ron("42 //"), + Err(SpannedError { + code: Error::UnclosedLineComment, + position: Position { line: 1, col: 6 }, + }) + ); + assert_eq!( + ron::from_str::<&RawValue>("42 //"), + Err(SpannedError { + code: Error::UnclosedLineComment, + position: Position { line: 1, col: 6 }, + }) + ); + assert_eq!( + ron::from_str::<&RawValue>("42 //\n").unwrap(), + RawValue::from_ron("42 //\n").unwrap() + ); + assert_eq!( + ron::from_str::<&RawValue>("42 /**/").unwrap(), + RawValue::from_ron("42 /**/").unwrap() + ); + assert_eq!( + ron::from_str::<&RawValue>("42 ").unwrap(), + RawValue::from_ron("42 ").unwrap() + ); + + assert_eq!( + RawValue::from_ron("a//"), + Err(SpannedError { + code: Error::UnclosedLineComment, + position: Position { line: 1, col: 4 }, + }) + ); + assert_eq!( + ron::from_str::<&RawValue>("a//"), + Err(SpannedError { + code: Error::UnclosedLineComment, + position: Position { line: 1, col: 4 }, + }) + ); + assert_eq!( + ron::from_str::<&RawValue>("a//\n").unwrap(), + RawValue::from_ron("a//\n").unwrap() + ); + assert_eq!( + ron::from_str::<&RawValue>("a/**/").unwrap(), + RawValue::from_ron("a/**/").unwrap() + ); + assert_eq!( + ron::from_str::<&RawValue>("a").unwrap(), + RawValue::from_ron("a").unwrap() + ); + + assert_eq!( + ron::to_string(&Some(Some(RawValue::from_ron("None").unwrap()))).unwrap(), + "Some(Some(None))" + ); + // Since a RawValue can contain anything, no implicit Some are allowed around it + assert_eq!( + ron::Options::default() + .with_default_extension(ron::extensions::Extensions::IMPLICIT_SOME) + .to_string(&Some(Some(RawValue::from_ron("None").unwrap()))) + .unwrap(), + "Some(Some(None))" + ); + assert_eq!( + ron::from_str::>>("Some(Some(None))").unwrap(), + Some(Some(RawValue::from_ron("None").unwrap())) + ); +} diff --git a/third_party/rust/ron/tests/425_escape_strings.rs b/third_party/rust/ron/tests/425_escape_strings.rs new file mode 100644 index 000000000000..93cd5066eb8d --- /dev/null +++ b/third_party/rust/ron/tests/425_escape_strings.rs @@ -0,0 +1,60 @@ +use ron::{ + de::from_str, + ser::{to_string, to_string_pretty, PrettyConfig}, +}; + +fn test_string_roundtrip(s: &str, config: Option) -> String { + let ser = match config { + Some(config) => to_string_pretty(s, config), + None => to_string(s), + } + .unwrap(); + + let de: String = from_str(&ser).unwrap(); + + assert_eq!(s, de); + + ser +} + +#[test] +fn test_escaped_string() { + let config = Some(PrettyConfig::default()); + + assert_eq!(test_string_roundtrip("a\nb", None), r#""a\nb""#); + assert_eq!(test_string_roundtrip("a\nb", config.clone()), r#""a\nb""#); + + assert_eq!(test_string_roundtrip("", None), "\"\""); + assert_eq!(test_string_roundtrip("", config.clone()), "\"\""); + + assert_eq!(test_string_roundtrip("\"", None), r#""\"""#); + assert_eq!(test_string_roundtrip("\"", config.clone()), r#""\"""#); + + assert_eq!(test_string_roundtrip("#", None), "\"#\""); + assert_eq!(test_string_roundtrip("#", config.clone()), "\"#\""); + + assert_eq!(test_string_roundtrip("\"#", None), r##""\"#""##); + assert_eq!(test_string_roundtrip("\"#", config.clone()), r##""\"#""##); + + assert_eq!(test_string_roundtrip("#\"#", None), r##""#\"#""##); + assert_eq!(test_string_roundtrip("#\"#", config.clone()), r##""#\"#""##); + + assert_eq!(test_string_roundtrip("#\"##", None), r###""#\"##""###); + assert_eq!(test_string_roundtrip("#\"##", config), r###""#\"##""###); +} + +#[test] +fn test_unescaped_string() { + let config = Some(PrettyConfig::default().escape_strings(false)); + + assert_eq!(test_string_roundtrip("a\nb", config.clone()), "\"a\nb\""); + assert_eq!(test_string_roundtrip("", config.clone()), "\"\""); + assert_eq!(test_string_roundtrip("\"", config.clone()), "r#\"\"\"#"); + assert_eq!(test_string_roundtrip("#", config.clone()), "\"#\""); + assert_eq!(test_string_roundtrip("\"#", config.clone()), "r##\"\"#\"##"); + assert_eq!( + test_string_roundtrip("#\"#", config.clone()), + "r##\"#\"#\"##" + ); + assert_eq!(test_string_roundtrip("#\"##", config), "r###\"#\"##\"###"); +} diff --git a/third_party/rust/ron/tests/436_untagged_bytes.rs b/third_party/rust/ron/tests/436_untagged_bytes.rs new file mode 100644 index 000000000000..c4d6bdda90a0 --- /dev/null +++ b/third_party/rust/ron/tests/436_untagged_bytes.rs @@ -0,0 +1,102 @@ +#[test] +fn test_serde_bytes() { + #[derive(Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)] + #[serde(rename = "b")] + struct BytesVal { + pub b: serde_bytes::ByteBuf, + } + + #[derive(Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)] + #[serde(untagged)] + enum Bad { + Bytes(BytesVal), + } + + let s = ron::to_string(&serde_bytes::Bytes::new(b"test")).unwrap(); + + assert_eq!(s, r#"b"test""#); + + let v: Bad = ron::from_str(r#"(b: b"test")"#).unwrap(); + + assert_eq!( + format!("{:?}", v), + "Bytes(BytesVal { b: [116, 101, 115, 116] })" + ); + + let s = ron::to_string(&v).unwrap(); + + assert_eq!(s, r#"(b:b"test")"#); +} + +#[test] +fn test_bytes() { + #[derive(Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)] + #[serde(rename = "b")] + struct BytesVal { + pub b: bytes::Bytes, + } + + #[derive(Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)] + #[serde(untagged)] + enum Bad { + Bytes(BytesVal), + } + + let s = ron::to_string(&bytes::Bytes::from("test")).unwrap(); + + assert_eq!(s, r#"b"test""#); + + let v: Bad = ron::from_str(r#"(b: b"test")"#).unwrap(); + + assert_eq!(format!("{:?}", v), r#"Bytes(BytesVal { b: b"test" })"#); + + let s = ron::to_string(&v).unwrap(); + + assert_eq!(s, r#"(b:b"test")"#); +} + +#[test] +fn test_strongly_typed_base64() { + use base64::engine::{general_purpose::STANDARD as BASE64, Engine}; + + enum Base64 {} + + impl Base64 { + fn serialize(data: &[u8], serializer: S) -> Result { + serializer.serialize_str(&BASE64.encode(data)) + } + + fn deserialize<'de, D: serde::Deserializer<'de>>( + deserializer: D, + ) -> Result, D::Error> { + let base64_str: &str = serde::Deserialize::deserialize(deserializer)?; + BASE64.decode(base64_str).map_err(serde::de::Error::custom) + } + } + + #[derive(Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)] + #[serde(rename = "b")] + struct BytesVal { + #[serde(with = "Base64")] + pub b: Vec, + } + + #[derive(Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)] + #[serde(untagged)] + enum Bad { + Bytes(BytesVal), + } + + let v: Bad = ron::from_str(r#"(b: "dGVzdA==")"#).unwrap(); + + assert_eq!( + v, + Bad::Bytes(BytesVal { + b: b"test".to_vec() + }) + ); + + let s = ron::to_string(&v).unwrap(); + + assert_eq!(s, r#"(b:"dGVzdA==")"#); +} diff --git a/third_party/rust/ron/tests/438_rusty_byte_strings.rs b/third_party/rust/ron/tests/438_rusty_byte_strings.rs new file mode 100644 index 000000000000..1a02446f4995 --- /dev/null +++ b/third_party/rust/ron/tests/438_rusty_byte_strings.rs @@ -0,0 +1,484 @@ +use ron::{ + error::{Position, SpannedError}, + Error, +}; +use serde::Deserialize; + +#[derive(Debug, Deserialize, PartialEq)] +struct BytesStruct { + small: Vec, + #[serde(with = "serde_bytes")] + large: Vec, +} + +#[test] +fn v_9_deprecated_base64_bytes_support() { + #![allow(deprecated)] + + // Requires padding of the base64 data + assert_eq!( + Ok(BytesStruct { + small: vec![1, 2], + large: vec![1, 2, 3, 4] + }), + ron::from_str("BytesStruct( small:[1, 2], large:\"AQIDBA==\" )"), + ); + + // Requires no padding of the base64 data + assert_eq!( + Ok(BytesStruct { + small: vec![1, 2], + large: vec![1, 2, 3, 4, 5, 6] + }), + ron::from_str("BytesStruct( small:[1, 2], large:r\"AQIDBAUG\" )"), + ); + + // Parsing an escaped byte string is also possible + assert_eq!( + Ok(BytesStruct { + small: vec![1, 2], + large: vec![1, 2, 3, 4, 5, 6] + }), + ron::from_str("BytesStruct( small:[1, 2], large:\"\\x41Q\\x49DBA\\x55G\" )"), + ); + + // Invalid base64 + assert_eq!( + Err(SpannedError { + code: Error::Base64Error(base64::DecodeError::InvalidByte(0, b'_')), + position: Position { line: 1, col: 40 } + }), + ron::from_str::("BytesStruct( small:[1, 2], large:\"_+!!\" )"), + ); + + // Invalid last base64 symbol + assert_eq!( + Err(SpannedError { + code: Error::Base64Error(base64::DecodeError::InvalidLastSymbol(1, b'x')), + position: Position { line: 1, col: 40 } + }), + ron::from_str::("BytesStruct( small:[1, 2], large:\"/x==\" )"), + ); + + // Missing padding + assert_eq!( + Err(SpannedError { + code: Error::Base64Error(base64::DecodeError::InvalidPadding), + position: Position { line: 1, col: 42 } + }), + ron::from_str::("BytesStruct( small:[1, 2], large:\"AQIDBA\" )"), + ); + + // Too much padding + assert_eq!( + Err(SpannedError { + code: Error::Base64Error(base64::DecodeError::InvalidByte(6, b'=')), + position: Position { line: 1, col: 45 } + }), + ron::from_str::("BytesStruct( small:[1, 2], large:\"AQIDBA===\" )"), + ); +} + +#[test] +fn rusty_byte_string() { + assert_eq!( + Ok(BytesStruct { + small: vec![1, 2], + large: vec![1, 2, 0, 4] + }), + ron::from_str("BytesStruct( small:[1, 2], large: b\"\\x01\\u{2}\\0\\x04\" )"), + ); + + assert_eq!( + ron::from_str::("\"Hello \\x01 \\u{2}!\"").unwrap(), + "Hello \x01 \u{2}!", + ); + assert_eq!( + &*ron::from_str::("b\"Hello \\x01 \\u{2}!\"").unwrap(), + b"Hello \x01 \x02!", + ); + + rusty_byte_string_roundtrip(b"hello", "b\"hello\"", "b\"hello\""); + rusty_byte_string_roundtrip(b"\"", "b\"\\\"\"", "br#\"\"\"#"); + rusty_byte_string_roundtrip(b"\"#", "b\"\\\"#\"", "br##\"\"#\"##"); + rusty_byte_string_roundtrip(b"\n", "b\"\\n\"", "b\"\n\""); + + assert_eq!( + ron::from_str::("b\"\\xf\"").unwrap_err(), + SpannedError { + code: Error::InvalidEscape("Non-hex digit found"), + position: Position { line: 1, col: 7 }, + }, + ); + assert_eq!( + ron::from_str::("b\"\\xf🦀\"").unwrap_err(), + SpannedError { + code: Error::InvalidEscape("Non-hex digit found"), + position: Position { line: 1, col: 7 }, + }, + ); + let err = ron::from_str::("br#q\"").unwrap_err(); + assert_eq!( + err, + SpannedError { + code: Error::ExpectedByteString, + position: Position { line: 1, col: 4 }, + }, + ); + assert_eq!(format!("{}", err.code), "Expected byte string",); + assert_eq!( + ron::from_str::("br#\"q").unwrap_err(), + SpannedError { + code: Error::ExpectedStringEnd, + position: Position { line: 1, col: 5 }, + }, + ); + assert_eq!( + ron::from_str::("r#q\"").unwrap_err(), + SpannedError { + code: Error::ExpectedString, + position: Position { line: 1, col: 3 }, + }, + ); + assert_eq!( + ron::from_str::("r#\"q").unwrap_err(), + SpannedError { + code: Error::ExpectedStringEnd, + position: Position { line: 1, col: 4 }, + }, + ); +} + +fn rusty_byte_string_roundtrip(bytes: &[u8], ron: &str, ron_raw: &str) { + let ser_list = ron::to_string(bytes).unwrap(); + let de_list: Vec = ron::from_str(&ser_list).unwrap(); + assert_eq!(de_list, bytes); + + let ser = ron::to_string(&bytes::Bytes::copy_from_slice(bytes)).unwrap(); + assert_eq!(ser, ron); + + let ser_non_raw = ron::ser::to_string_pretty( + &bytes::Bytes::copy_from_slice(bytes), + ron::ser::PrettyConfig::default(), + ) + .unwrap(); + assert_eq!(ser_non_raw, ron); + + let ser_raw = ron::ser::to_string_pretty( + &bytes::Bytes::copy_from_slice(bytes), + ron::ser::PrettyConfig::default().escape_strings(false), + ) + .unwrap(); + assert_eq!(ser_raw, ron_raw); + + let de: bytes::Bytes = ron::from_str(&ser).unwrap(); + assert_eq!(de, bytes); + + let de_raw: bytes::Bytes = ron::from_str(&ser_raw).unwrap(); + assert_eq!(de_raw, bytes); +} + +#[test] +fn fuzzer_failures() { + assert_eq!( + ron::to_string(&bytes::Bytes::copy_from_slice(&[ + 123, 0, 0, 0, 0, 214, 214, 214, 214, 214 + ])) + .unwrap(), + r#"b"{\x00\x00\x00\x00\xd6\xd6\xd6\xd6\xd6""# + ); + // Need to fall back to escaping so no invalid UTF-8 is produced + assert_eq!( + ron::ser::to_string_pretty( + &bytes::Bytes::copy_from_slice(&[123, 0, 0, 0, 0, 214, 214, 214, 214, 214]), + ron::ser::PrettyConfig::default().escape_strings(false) + ) + .unwrap(), + r#"b"{\x00\x00\x00\x00\xd6\xd6\xd6\xd6\xd6""# + ); + + assert_eq!( + ron::to_string(&bytes::Bytes::copy_from_slice(&[123, 0, 0, 0, 0])).unwrap(), + r#"b"{\x00\x00\x00\x00""# + ); + assert_eq!( + ron::ser::to_string_pretty( + &bytes::Bytes::copy_from_slice(&[123, 0, 0, 0, 0]), + ron::ser::PrettyConfig::default().escape_strings(false) + ) + .unwrap(), + "b\"{\x00\x00\x00\x00\"" + ); + + // `br#` should be parsed as the start of a byte string, not the identifier `br` and a `#` + assert_eq!( + ron::from_str(r##"br#"""#"##), + Ok(ron::Value::Bytes(vec![34])) + ); + assert_eq!( + ron::from_str(r##"{"error": br#"""#}"##), + Ok(ron::Value::Map( + [( + ron::Value::String(String::from("error")), + ron::Value::Bytes(vec![34]) + )] + .into_iter() + .collect() + )) + ); + assert_eq!( + ron::from_str( + r##"#![enable(unwrap_newtypes)] + #![enable(unwrap_variant_newtypes)] + Some({"error": br#"""#}) + "## + ), + Ok(ron::Value::Option(Some(Box::new(ron::Value::Map( + [( + ron::Value::String(String::from("error")), + ron::Value::Bytes(vec![34]) + )] + .into_iter() + .collect() + ))))) + ); + + // `br"` should be parsed as the start of a byte string, not the identifier `br` and a `"` + assert_eq!(ron::from_str(r#"br"""#), Ok(ron::Value::Bytes(vec![]))); + assert_eq!( + ron::from_str(r#"{"error": br""}"#), + Ok(ron::Value::Map( + [( + ron::Value::String(String::from("error")), + ron::Value::Bytes(vec![]) + )] + .into_iter() + .collect() + )) + ); + assert_eq!( + ron::from_str( + r#"#![enable(unwrap_newtypes)] + #![enable(unwrap_variant_newtypes)] + Some({"error": br""}) + "# + ), + Ok(ron::Value::Option(Some(Box::new(ron::Value::Map( + [( + ron::Value::String(String::from("error")), + ron::Value::Bytes(vec![]) + )] + .into_iter() + .collect() + ))))) + ); + + // Test that the struct type check for newtype variant unwrapping does + // not enter inside a byte string to find a bad comment start + assert_eq!( + ron::from_str::>( + r#"#![enable(unwrap_variant_newtypes)] Some(b"\xff/not a comment")"# + ) + .unwrap(), + Some(ron::Value::Bytes(b"\xff/not a comment".to_vec())) + ); + + // `b'` should be parsed as the start of a byte literal, not the identifier `b` and a `'` + assert_eq!( + ron::from_str(r"b'\xff'"), + Ok(ron::Value::Number(ron::value::Number::U8(b'\xff'))) + ); + + // `b`, `br`, `bq`, and `brq` should all be parsed as identifiers + for id in ["b", "br", "bq", "brq"] { + assert_eq!(ron::from_str(id), Ok(ron::Value::Unit)); + } +} + +#[test] +fn serialize_backslash_byte_string() { + check_roundtrip('\\', r"'\\'", r"'\\'"); + check_roundtrip( + bytes::Bytes::copy_from_slice(b"\\"), + r#"b"\\""#, + "br#\"\\\"#", + ); +} + +fn check_roundtrip< + T: PartialEq + std::fmt::Debug + serde::Serialize + serde::de::DeserializeOwned, +>( + val: T, + cmp: &str, + cmp_raw: &str, +) { + let ron = ron::to_string(&val).unwrap(); + assert_eq!(ron, cmp); + + let ron_escaped = + ron::ser::to_string_pretty(&val, ron::ser::PrettyConfig::default().escape_strings(true)) + .unwrap(); + assert_eq!(ron_escaped, cmp); + + let ron_raw = ron::ser::to_string_pretty( + &val, + ron::ser::PrettyConfig::default().escape_strings(false), + ) + .unwrap(); + assert_eq!(ron_raw, cmp_raw); + + let de = ron::from_str::(&ron).unwrap(); + assert_eq!(de, val); + + let de_raw = ron::from_str::(&ron_raw).unwrap(); + assert_eq!(de_raw, val); +} + +#[test] +fn test_weird_escapes() { + assert_eq!( + ron::from_str::(r#""\u{1F980}""#), + Ok(String::from("\u{1F980}")) + ); + assert_eq!( + ron::from_str::(r#"b"\xf0\x9f\xa6\x80""#), + Ok(bytes::Bytes::copy_from_slice("\u{1F980}".as_bytes())) + ); + assert_eq!( + ron::from_str::(r#""\xf0\x9f\xa6\x80""#), + Ok(String::from("\u{1F980}")) + ); + assert_eq!( + ron::from_str::(r#""\xf0""#), + Err(SpannedError { + code: Error::InvalidEscape("Not a valid byte-escaped Unicode character"), + position: Position { line: 1, col: 6 } + }) + ); + assert_eq!( + ron::from_str::(r#""\xf0\x9f""#), + Err(SpannedError { + code: Error::InvalidEscape("Not a valid byte-escaped Unicode character"), + position: Position { line: 1, col: 10 } + }) + ); + assert_eq!( + ron::from_str::(r#""\xf0\x9f\x40""#), + Err(SpannedError { + code: Error::InvalidEscape("Not a valid byte-escaped Unicode character"), + position: Position { line: 1, col: 14 } + }) + ); + assert_eq!( + ron::from_str::(r#""\xf0\x9f\xa6""#), + Err(SpannedError { + code: Error::InvalidEscape("Not a valid byte-escaped Unicode character"), + position: Position { line: 1, col: 14 } + }) + ); + assert_eq!( + ron::from_str::(r#""\xff\xff\xff\xff""#), + Err(SpannedError { + code: Error::InvalidEscape("Not a valid byte-escaped Unicode character"), + position: Position { line: 1, col: 18 } + }) + ); + + assert_eq!(ron::from_str::(r"'\u{1F980}'"), Ok('\u{1F980}')); + assert_eq!( + ron::from_str::(r"'\xf0\x9f\xa6\x80'"), + Err(SpannedError { + code: Error::InvalidEscape("Not a valid byte-escaped Unicode character"), + position: Position { line: 1, col: 6 } + }) + ); +} + +#[test] +fn byte_literal() { + assert_eq!( + ron::from_str("b'\0'"), + Ok(ron::Value::Number(ron::value::Number::U8(0))) + ); + assert_eq!( + ron::from_str("b'\\0'"), + Ok(ron::Value::Number(ron::value::Number::U8(0))) + ); + + for b in 0..=255_u8 { + let default = std::ascii::escape_default(b) + .map(char::from) + .collect::(); + let lower = format!(r"\x{:02x}", b); + let upper = format!(r"\x{:02X}", b); + + assert_eq!( + ron::from_str(&format!("b'{}'", default)), + Ok(ron::Value::Number(ron::value::Number::U8(b))) + ); + assert_eq!( + ron::from_str(&format!("b'{}'", lower)), + Ok(ron::Value::Number(ron::value::Number::U8(b))) + ); + assert_eq!( + ron::from_str(&format!("b'{}'", upper)), + Ok(ron::Value::Number(ron::value::Number::U8(b))) + ); + } + + assert_eq!( + ron::from_str::(r#"b'\u{0}'"#), + Err(SpannedError { + code: Error::InvalidEscape("Unexpected Unicode escape in byte literal"), + position: Position { line: 1, col: 8 }, + }) + ); + + let err = ron::from_str::(r#"b'🦀'"#).unwrap_err(); + assert_eq!( + err, + SpannedError { + code: Error::ExpectedByteLiteral, + position: Position { line: 1, col: 4 }, + } + ); + assert_eq!(format!("{}", err.code), "Expected byte literal"); + + assert_eq!( + ron::from_str::(r#"b'qq'"#).unwrap_err(), + SpannedError { + code: Error::ExpectedByteLiteral, + position: Position { line: 1, col: 4 }, + } + ); + + assert_eq!( + ron::from_str::(r#"b'9'"#), + Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("an 8-bit signed integer"), + found: String::from(r#"b'9'"#) + }, + position: Position { line: 1, col: 5 }, + }) + ); +} + +#[test] +fn invalid_identifier() { + #[allow(dead_code)] + #[derive(Debug, Deserialize)] // GRCOV_EXCL_LINE + struct Test { + a: i32, + } + + for id in ["b\"", "b'", "br#", "br\"", "r\"", "r#\"", "r##"] { + assert_eq!( + ron::from_str::(&format!("({}: 42)", id)).unwrap_err(), + SpannedError { + code: Error::ExpectedIdentifier, + position: Position { line: 1, col: 2 }, + } + ); + } +} diff --git a/third_party/rust/ron/tests/447_compact_maps_structs.rs b/third_party/rust/ron/tests/447_compact_maps_structs.rs new file mode 100644 index 000000000000..1185acd721b7 --- /dev/null +++ b/third_party/rust/ron/tests/447_compact_maps_structs.rs @@ -0,0 +1,39 @@ +use std::collections::BTreeMap; + +use ron::ser::{to_string, to_string_pretty, PrettyConfig}; + +#[derive(serde_derive::Serialize)] +struct Struct { + a: u8, + b: u8, +} + +#[test] +fn compact_structs() { + let s = Struct { a: 4, b: 2 }; + + assert_eq!(to_string(&s).unwrap(), "(a:4,b:2)"); + assert_eq!( + to_string_pretty(&s, PrettyConfig::default()).unwrap(), + "(\n a: 4,\n b: 2,\n)" + ); + assert_eq!( + to_string_pretty(&s, PrettyConfig::default().compact_structs(true)).unwrap(), + "(a: 4, b: 2)" + ); +} + +#[test] +fn compact_maps() { + let m: BTreeMap<&str, i32> = BTreeMap::from_iter([("a", 4), ("b", 2)]); + + assert_eq!(to_string(&m).unwrap(), "{\"a\":4,\"b\":2}"); + assert_eq!( + to_string_pretty(&m, PrettyConfig::default()).unwrap(), + "{\n \"a\": 4,\n \"b\": 2,\n}" + ); + assert_eq!( + to_string_pretty(&m, PrettyConfig::default().compact_maps(true)).unwrap(), + "{\"a\": 4, \"b\": 2}" + ); +} diff --git a/third_party/rust/ron/tests/449_tagged_enum.rs b/third_party/rust/ron/tests/449_tagged_enum.rs new file mode 100644 index 000000000000..907e5952ac79 --- /dev/null +++ b/third_party/rust/ron/tests/449_tagged_enum.rs @@ -0,0 +1,353 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] +enum InnerEnum { + Unit, + Newtype(bool), + Tuple(bool, i32), + Struct { field: char }, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] +#[serde(deny_unknown_fields)] +struct Container { + field: InnerEnum, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] +enum OuterEnum { + Variant(Container), + Sum { field: InnerEnum, value: i32 }, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] +#[serde(tag = "tag")] +enum OuterEnumInternal { + Variant(Container), + Sum { field: InnerEnum, value: i32 }, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] +#[serde(tag = "tag", content = "c")] +enum OuterEnumAdjacent { + Variant(Container), + Sum { field: InnerEnum, value: i32 }, +} + +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)] +#[serde(untagged)] +enum OuterEnumUntagged { + Variant(Container), + Sum { field: InnerEnum, value: i32 }, +} + +#[test] +fn test_serde_content_hack() { + assert_eq!( + std::any::type_name::(), + "serde::__private::de::content::Content" + ); +} + +#[test] +fn test_serde_internally_tagged_hack() { + const SERDE_CONTENT_CANARY: &str = "serde::__private::de::content::Content"; + const SERDE_TAG_KEY_CANARY: &str = "serde::__private::de::content::TagOrContent"; + + struct Deserializer { + tag_key: Option, + tag_value: String, + field_key: Option, + field_value: i32, + } + + impl<'de> serde::Deserializer<'de> for Deserializer { + type Error = ron::Error; + + fn deserialize_any(self, visitor: V) -> Result + where + V: serde::de::Visitor<'de>, + { + visitor.visit_map(self) + } + + // GRCOV_EXCL_START + serde::forward_to_deserialize_any! { + bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string + bytes byte_buf option unit unit_struct newtype_struct seq tuple + tuple_struct map struct enum identifier ignored_any + } + // GRCOV_EXCL_STOP + } + + impl<'de> serde::de::MapAccess<'de> for Deserializer { + type Error = ron::Error; + + fn next_key_seed(&mut self, seed: K) -> Result, Self::Error> + where + K: serde::de::DeserializeSeed<'de>, + { + assert_eq!(std::any::type_name::(), SERDE_TAG_KEY_CANARY); + + if let Some(tag_key) = self.tag_key.take() { + return seed + .deserialize(serde::de::value::StringDeserializer::new(tag_key)) + .map(Some); + } + + if let Some(field_key) = self.field_key.take() { + return seed + .deserialize(serde::de::value::StringDeserializer::new(field_key)) + .map(Some); + } + + Ok(None) + } + + fn next_value_seed(&mut self, seed: V) -> Result + where + V: serde::de::DeserializeSeed<'de>, + { + if self.field_key.is_some() { + assert_ne!(std::any::type_name::(), SERDE_CONTENT_CANARY); + return seed.deserialize(serde::de::value::StrDeserializer::new(&self.tag_value)); + } + + assert_eq!(std::any::type_name::(), SERDE_CONTENT_CANARY); + + seed.deserialize(serde::de::value::I32Deserializer::new(self.field_value)) + } + } + + #[derive(PartialEq, Debug, Deserialize)] + #[serde(tag = "tag")] + enum InternallyTagged { + A { hi: i32 }, + } + + assert_eq!( + InternallyTagged::deserialize(Deserializer { + tag_key: Some(String::from("tag")), + tag_value: String::from("A"), + field_key: Some(String::from("hi")), + field_value: 42, + }), + Ok(InternallyTagged::A { hi: 42 }) + ); +} + +#[test] +fn test_enum_in_enum_roundtrip() { + let outer = OuterEnum::Variant(Container { + field: InnerEnum::Unit, + }); + + let ron = ron::to_string(&outer).unwrap(); + + assert_eq!(ron, "Variant((field:Unit))"); + + let de = ron::from_str::(&ron); + + assert_eq!(de, Ok(outer)); + + let outer = OuterEnum::Sum { + field: InnerEnum::Newtype(true), + value: 42, + }; + + let ron = ron::to_string(&outer).unwrap(); + + assert_eq!(ron, "Sum(field:Newtype(true),value:42)"); + + let de = ron::from_str::(&ron); + + assert_eq!(de, Ok(outer)); + + let outer = OuterEnum::Sum { + field: InnerEnum::Tuple(true, 24), + value: 42, + }; + + let ron = ron::to_string(&outer).unwrap(); + + assert_eq!(ron, "Sum(field:Tuple(true,24),value:42)"); + + let de = ron::from_str::(&ron); + + assert_eq!(de, Ok(outer)); + + let outer = OuterEnum::Sum { + field: InnerEnum::Struct { field: '🦀' }, + value: 42, + }; + + let ron = ron::to_string(&outer).unwrap(); + + assert_eq!(ron, "Sum(field:Struct(field:'🦀'),value:42)"); + + let de = ron::from_str::(&ron); + + assert_eq!(de, Ok(outer)); +} + +#[test] +fn test_enum_in_internally_tagged_roundtrip() { + let outer = OuterEnumInternal::Variant(Container { + field: InnerEnum::Unit, + }); + + let ron = ron::to_string(&outer).unwrap(); + + assert_eq!(ron, "(tag:\"Variant\",field:Unit)"); + + let de = ron::from_str::(&ron); + + assert_eq!(de, Ok(outer)); + + let outer = OuterEnumInternal::Sum { + field: InnerEnum::Newtype(true), + value: 42, + }; + + let ron = ron::to_string(&outer).unwrap(); + + assert_eq!(ron, "(tag:\"Sum\",field:Newtype(true),value:42)"); + + let de = ron::from_str::(&ron); + + assert_eq!(de, Ok(outer)); + + let outer = OuterEnumInternal::Sum { + field: InnerEnum::Tuple(true, 24), + value: 42, + }; + + let ron = ron::to_string(&outer).unwrap(); + + assert_eq!(ron, "(tag:\"Sum\",field:Tuple(true,24),value:42)"); + + let de = ron::from_str::(&ron); + + assert_eq!(de, Ok(outer)); + + let outer = OuterEnumInternal::Sum { + field: InnerEnum::Struct { field: '🦀' }, + value: 42, + }; + + let ron = ron::to_string(&outer).unwrap(); + + assert_eq!(ron, "(tag:\"Sum\",field:Struct(field:'🦀'),value:42)"); + + let de = ron::from_str::(&ron); + + assert_eq!(de, Ok(outer)); +} + +#[test] +fn test_enum_in_adjacently_tagged_roundtrip() { + let outer = OuterEnumAdjacent::Variant(Container { + field: InnerEnum::Unit, + }); + + let ron = ron::to_string(&outer).unwrap(); + + assert_eq!(ron, "(tag:Variant,c:(field:Unit))"); + + let de = ron::from_str::(&ron); + + assert_eq!(de, Ok(outer)); + + let outer = OuterEnumAdjacent::Sum { + field: InnerEnum::Newtype(true), + value: 42, + }; + + let ron = ron::to_string(&outer).unwrap(); + + assert_eq!(ron, "(tag:Sum,c:(field:Newtype(true),value:42))"); + + let de = ron::from_str::(&ron); + + assert_eq!(de, Ok(outer)); + + let outer = OuterEnumAdjacent::Sum { + field: InnerEnum::Tuple(true, 24), + value: 42, + }; + + let ron = ron::to_string(&outer).unwrap(); + + assert_eq!(ron, "(tag:Sum,c:(field:Tuple(true,24),value:42))"); + + let de = ron::from_str::(&ron); + + assert_eq!(de, Ok(outer)); + + let outer = OuterEnumAdjacent::Sum { + field: InnerEnum::Struct { field: '🦀' }, + value: 42, + }; + + let ron = ron::to_string(&outer).unwrap(); + + assert_eq!(ron, "(tag:Sum,c:(field:Struct(field:'🦀'),value:42))"); + + let de = ron::from_str::(&ron); + + assert_eq!(de, Ok(outer)); +} + +#[test] +fn test_enum_in_untagged_roundtrip() { + let outer = OuterEnumUntagged::Variant(Container { + field: InnerEnum::Unit, + }); + + let ron = ron::to_string(&outer).unwrap(); + + assert_eq!(ron, "(field:Unit)"); + + let de = ron::from_str::(&ron); + + assert_eq!(de, Ok(outer)); + + let outer = OuterEnumUntagged::Sum { + field: InnerEnum::Newtype(true), + value: 42, + }; + + let ron = ron::to_string(&outer).unwrap(); + + assert_eq!(ron, "(field:Newtype(true),value:42)"); + + let de = ron::from_str::(&ron); + + assert_eq!(de, Ok(outer)); + + let outer = OuterEnumUntagged::Sum { + field: InnerEnum::Tuple(true, 24), + value: 42, + }; + + let ron = ron::to_string(&outer).unwrap(); + + assert_eq!(ron, "(field:Tuple(true,24),value:42)"); + + let de = ron::from_str::(&ron); + + assert_eq!(de, Ok(outer)); + + let outer = OuterEnumUntagged::Sum { + field: InnerEnum::Struct { field: '🦀' }, + value: 42, + }; + + let ron = ron::to_string(&outer).unwrap(); + + assert_eq!(ron, "(field:Struct(field:'🦀'),value:42)"); + + let de = ron::from_str::(&ron); + + assert_eq!(de, Ok(outer)); +} diff --git a/third_party/rust/ron/tests/462_bytes.rs b/third_party/rust/ron/tests/462_bytes.rs index ac91890397cc..832dc6a162ac 100644 --- a/third_party/rust/ron/tests/462_bytes.rs +++ b/third_party/rust/ron/tests/462_bytes.rs @@ -14,7 +14,7 @@ fn test_deserialise_byte_slice() { Err(ron::error::SpannedError { code: ron::error::Error::InvalidValueForType { expected: String::from("a borrowed byte array"), - found: String::from("the bytes \"AAECAw==\""), + found: String::from("the byte string b\"\\x00\\x01\\x02\\x03\""), }, position: ron::error::Position { line: 1, col: 10 }, }) diff --git a/third_party/rust/ron/tests/465_implicit_some_stack.rs b/third_party/rust/ron/tests/465_implicit_some_stack.rs new file mode 100644 index 000000000000..3e6a582f8cdb --- /dev/null +++ b/third_party/rust/ron/tests/465_implicit_some_stack.rs @@ -0,0 +1,25 @@ +#[test] +fn roundtrip_implicit_some_stack() { + check_roundtrip(Option::<()>::None, "None"); + check_roundtrip(Some(()), "()"); + check_roundtrip(Some(Some(())), "()"); + check_roundtrip(Some(Some(Some(()))), "()"); + check_roundtrip(Some(Option::<()>::None), "Some(None)"); + check_roundtrip(Some(Some(Option::<()>::None)), "Some(Some(None))"); +} + +fn check_roundtrip< + T: PartialEq + std::fmt::Debug + serde::Serialize + serde::de::DeserializeOwned, +>( + val: T, + check: &str, +) { + let options = + ron::Options::default().with_default_extension(ron::extensions::Extensions::IMPLICIT_SOME); + + let ron = options.to_string(&val).unwrap(); + assert_eq!(ron, check); + + let de: T = options.from_str(&ron).unwrap(); + assert_eq!(de, val); +} diff --git a/third_party/rust/ron/tests/465_no_comment_char_value.rs b/third_party/rust/ron/tests/465_no_comment_char_value.rs new file mode 100644 index 000000000000..4d77f0ad1d55 --- /dev/null +++ b/third_party/rust/ron/tests/465_no_comment_char_value.rs @@ -0,0 +1,13 @@ +#[test] +fn value_deserialises_r_name() { + // deserialising "A('/')" into ron::Value previously failed as the struct type + // searcher reads into the char and then finds a weird comment starter there + assert_eq!( + ron::from_str("A('/')"), + Ok(ron::Value::Seq(vec![ron::Value::Char('/')])) + ); + assert_eq!( + ron::from_str("A(\"/\")"), + Ok(ron::Value::Seq(vec![ron::Value::String(String::from("/"))])) + ); +} diff --git a/third_party/rust/ron/tests/465_r_name_value.rs b/third_party/rust/ron/tests/465_r_name_value.rs new file mode 100644 index 000000000000..d6a63bc5032b --- /dev/null +++ b/third_party/rust/ron/tests/465_r_name_value.rs @@ -0,0 +1,22 @@ +#[test] +fn value_deserialises_r_name() { + assert_eq!(ron::from_str("r"), Ok(ron::Value::Unit)); + assert_eq!(ron::from_str("r()"), Ok(ron::Value::Seq(vec![]))); + assert_eq!( + ron::from_str("r(42)"), + Ok(ron::Value::Seq(vec![ron::Value::Number( + ron::value::Number::U8(42) + )])) + ); + assert_eq!( + ron::from_str("r(a:42)"), + Ok(ron::Value::Map( + [( + ron::Value::String(String::from("a")), + ron::Value::Number(ron::value::Number::U8(42)) + )] + .into_iter() + .collect() + )) + ); +} diff --git a/third_party/rust/ron/tests/465_ser_backslash_string.rs b/third_party/rust/ron/tests/465_ser_backslash_string.rs new file mode 100644 index 000000000000..5ca35450d073 --- /dev/null +++ b/third_party/rust/ron/tests/465_ser_backslash_string.rs @@ -0,0 +1,34 @@ +#[test] +fn serialize_backslash_string() { + check_roundtrip('\\', r"'\\'", r"'\\'"); + check_roundtrip(String::from("\\"), r#""\\""#, "r#\"\\\"#"); +} + +fn check_roundtrip< + T: PartialEq + std::fmt::Debug + serde::Serialize + serde::de::DeserializeOwned, +>( + val: T, + cmp: &str, + cmp_raw: &str, +) { + let ron = ron::to_string(&val).unwrap(); + assert_eq!(ron, cmp); + + let ron_escaped = + ron::ser::to_string_pretty(&val, ron::ser::PrettyConfig::default().escape_strings(true)) + .unwrap(); + assert_eq!(ron_escaped, cmp); + + let ron_raw = ron::ser::to_string_pretty( + &val, + ron::ser::PrettyConfig::default().escape_strings(false), + ) + .unwrap(); + assert_eq!(ron_raw, cmp_raw); + + let de = ron::from_str::(&ron).unwrap(); + assert_eq!(de, val); + + let de_raw = ron::from_str::(&ron_raw).unwrap(); + assert_eq!(de_raw, val); +} diff --git a/third_party/rust/ron/tests/465_unwrap_some_newtype_variant_value.rs b/third_party/rust/ron/tests/465_unwrap_some_newtype_variant_value.rs new file mode 100644 index 000000000000..512aaf8d6e15 --- /dev/null +++ b/third_party/rust/ron/tests/465_unwrap_some_newtype_variant_value.rs @@ -0,0 +1,44 @@ +#[test] +fn deserialise_value_with_unwrap_some_newtype_variant() { + assert_eq!( + ron::from_str::("Some(a: 42)"), + Err(ron::error::SpannedError { + code: ron::Error::ExpectedOptionEnd, + position: ron::error::Position { line: 1, col: 7 }, + }), + ); + assert_eq!( + ron::from_str("#![enable(unwrap_variant_newtypes)] Some(a: 42)"), + Ok(ron::Value::Option(Some(Box::new(ron::Value::Map( + [( + ron::Value::String(String::from("a")), + ron::Value::Number(ron::value::Number::U8(42)) + )] + .into_iter() + .collect() + ))))), + ); + assert_eq!( + ron::from_str("#![enable(unwrap_variant_newtypes)] Some(42, true)"), + Ok(ron::Value::Option(Some(Box::new(ron::Value::Seq(vec![ + ron::Value::Number(ron::value::Number::U8(42)), + ron::Value::Bool(true) + ]))))), + ); + assert_eq!( + ron::from_str("#![enable(unwrap_variant_newtypes)] Some(42,)"), + Ok(ron::Value::Option(Some(Box::new(ron::Value::Number( + ron::value::Number::U8(42) + ))))), + ); + assert_eq!( + ron::from_str("#![enable(unwrap_variant_newtypes)] Some()"), + Ok(ron::Value::Option(Some(Box::new(ron::Value::Unit)))), + ); + assert_eq!( + ron::from_str("#![enable(unwrap_variant_newtypes)] Some(42)"), + Ok(ron::Value::Option(Some(Box::new(ron::Value::Number( + ron::value::Number::U8(42) + ))))), + ); +} diff --git a/third_party/rust/ron/tests/465_validate_ser_identifiers.rs b/third_party/rust/ron/tests/465_validate_ser_identifiers.rs new file mode 100644 index 000000000000..c7e8628fbd22 --- /dev/null +++ b/third_party/rust/ron/tests/465_validate_ser_identifiers.rs @@ -0,0 +1,89 @@ +use serde::ser::{SerializeStruct, SerializeStructVariant, Serializer}; + +#[test] +fn invalid_struct_name() { + let mut ser = ron::Serializer::new(String::new(), None).unwrap(); + + assert_eq!( + ser.serialize_newtype_struct("", &true).err(), + Some(ron::Error::InvalidIdentifier(String::from(""))), + ); + + assert_eq!( + ser.serialize_tuple_struct("", 0).err(), + Some(ron::Error::InvalidIdentifier(String::from(""))), + ); + + assert_eq!( + ser.serialize_struct("", 0).err(), + Some(ron::Error::InvalidIdentifier(String::from(""))), + ); +} + +#[test] +fn invalid_enum_variant_name() { + let mut ser = ron::Serializer::new(String::new(), None).unwrap(); + + assert_eq!( + ser.serialize_unit_variant("", 0, "A").err(), + Some(ron::Error::InvalidIdentifier(String::from(""))), + ); + + assert_eq!( + ser.serialize_unit_variant("A", 0, "").err(), + Some(ron::Error::InvalidIdentifier(String::from(""))), + ); + + assert_eq!( + ser.serialize_newtype_variant("", 0, "A", &true).err(), + Some(ron::Error::InvalidIdentifier(String::from(""))), + ); + + assert_eq!( + ser.serialize_newtype_variant("A", 0, "", &true).err(), + Some(ron::Error::InvalidIdentifier(String::from(""))), + ); + + assert_eq!( + ser.serialize_tuple_variant("", 0, "A", 0).err(), + Some(ron::Error::InvalidIdentifier(String::from(""))), + ); + + assert_eq!( + ser.serialize_tuple_variant("A", 0, "", 0).err(), + Some(ron::Error::InvalidIdentifier(String::from(""))), + ); + + assert_eq!( + ser.serialize_struct_variant("", 0, "A", 0).err(), + Some(ron::Error::InvalidIdentifier(String::from(""))), + ); + + assert_eq!( + ser.serialize_struct_variant("A", 0, "", 0).err(), + Some(ron::Error::InvalidIdentifier(String::from(""))), + ); +} + +#[test] +fn invalid_struct_field_name() { + let mut ser = ron::Serializer::new(String::new(), None).unwrap(); + + let mut r#struct = ser.serialize_struct("A", 2).unwrap(); + SerializeStruct::serialize_field(&mut r#struct, "A", &true).unwrap(); + + assert_eq!( + SerializeStruct::serialize_field(&mut r#struct, "", &true).err(), + Some(ron::Error::InvalidIdentifier(String::from(""))), + ); + + std::mem::drop(r#struct); + + let mut r#struct = ser.serialize_struct_variant("A", 0, "A", 2).unwrap(); + SerializeStructVariant::serialize_field(&mut r#struct, "A", &true).unwrap(); + + assert_eq!( + SerializeStructVariant::serialize_field(&mut r#struct, "", &true).err(), + Some(ron::Error::InvalidIdentifier(String::from(""))), + ); +} diff --git a/third_party/rust/ron/tests/481_number_underscores_suffixes.rs b/third_party/rust/ron/tests/481_number_underscores_suffixes.rs new file mode 100644 index 000000000000..b79441efda91 --- /dev/null +++ b/third_party/rust/ron/tests/481_number_underscores_suffixes.rs @@ -0,0 +1,649 @@ +use ron::Number; + +#[test] +#[allow(clippy::unusual_byte_groupings)] +#[allow(clippy::inconsistent_digit_grouping)] +#[allow(clippy::zero_prefixed_literal)] +fn de_integer_underscores() { + assert_eq!(ron::from_str("0b10_10___101_"), Ok(0b10_10___101__u8)); + assert_eq!( + ron::from_str::("_0b1"), + Err(ron::error::SpannedError { + code: ron::Error::UnderscoreAtBeginning, + position: ron::error::Position { line: 1, col: 1 }, + }) + ); + assert_eq!( + ron::from_str::("_0b1_u8"), + Err(ron::error::SpannedError { + code: ron::Error::UnderscoreAtBeginning, + position: ron::error::Position { line: 1, col: 1 }, + }) + ); + assert_eq!( + ron::from_str::("0b2"), + Err(ron::error::SpannedError { + code: ron::Error::InvalidIntegerDigit { + digit: '2', + base: 2 + }, + position: ron::error::Position { line: 1, col: 3 }, + }) + ); + assert_eq!( + ron::from_str::("-0b2_i32"), + Err(ron::error::SpannedError { + code: ron::Error::InvalidIntegerDigit { + digit: '2', + base: 2 + }, + position: ron::error::Position { line: 1, col: 4 }, + }) + ); + + assert_eq!(ron::from_str("0o71_32___145_"), Ok(0o71_32___145_)); + assert_eq!( + ron::from_str::("_0o5"), + Err(ron::error::SpannedError { + code: ron::Error::UnderscoreAtBeginning, + position: ron::error::Position { line: 1, col: 1 }, + }) + ); + assert_eq!( + ron::from_str::("0oA"), + Err(ron::error::SpannedError { + code: ron::Error::InvalidIntegerDigit { + digit: 'A', + base: 8 + }, + position: ron::error::Position { line: 1, col: 3 }, + }) + ); + + assert_eq!(ron::from_str("0xa1_fe___372_"), Ok(0xa1_fe___372_)); + assert_eq!( + ron::from_str::("_0xF"), + Err(ron::error::SpannedError { + code: ron::Error::UnderscoreAtBeginning, + position: ron::error::Position { line: 1, col: 1 }, + }) + ); + assert_eq!( + ron::from_str::("0xZ"), + Err(ron::error::SpannedError { + code: ron::Error::ExpectedInteger, + position: ron::error::Position { line: 1, col: 3 }, + }) + ); + + assert_eq!(ron::from_str("0_6_163_810___17"), Ok(0_6_163_810___17)); + assert_eq!( + ron::from_str::("_123"), + Err(ron::error::SpannedError { + code: ron::Error::UnderscoreAtBeginning, + position: ron::error::Position { line: 1, col: 1 }, + }) + ); + assert_eq!( + ron::from_str::("12a"), + Err(ron::error::SpannedError { + code: ron::Error::InvalidIntegerDigit { + digit: 'a', + base: 10 + }, + position: ron::error::Position { line: 1, col: 3 }, + }) + ); +} + +#[test] +#[allow(clippy::inconsistent_digit_grouping)] +fn de_float_underscores() { + assert_eq!(ron::from_str("2_18__6_"), Ok(2_18__6__f32)); + assert_eq!( + ron::from_str::("_286"), + Err(ron::error::SpannedError { + code: ron::Error::UnderscoreAtBeginning, + position: ron::error::Position { line: 1, col: 1 }, + }) + ); + assert_eq!( + ron::from_str::("2a86"), + Err(ron::error::SpannedError { + code: ron::Error::TrailingCharacters, + position: ron::error::Position { line: 1, col: 2 }, + }) + ); + + assert_eq!(ron::from_str("2_18__6_."), Ok(2_18__6__f32)); + assert_eq!( + ron::from_str::("2_18__6_._"), + Err(ron::error::SpannedError { + code: ron::Error::FloatUnderscore, + position: ron::error::Position { line: 1, col: 10 }, + }) + ); + assert_eq!( + ron::from_str::("2_18__6_.3__7_"), + Ok(2_18__6_.3__7__f32) + ); + + assert_eq!(ron::from_str::(".3__7_"), Ok(0.3__7__f32)); + assert_eq!( + ron::from_str::("._3__7_"), + Err(ron::error::SpannedError { + code: ron::Error::FloatUnderscore, + position: ron::error::Position { line: 1, col: 2 }, + }) + ); + + assert_eq!( + ron::from_str::("2_18__6_.3__7_e____7_3__"), + Ok(2_18__6_.3__7_e____7_3___f64) + ); + assert_eq!( + ron::from_str::("2_18__6_.3__7_e+____"), + Err(ron::error::SpannedError { + code: ron::Error::ExpectedFloat, + position: ron::error::Position { line: 1, col: 1 }, + }) + ); +} + +#[test] +fn value_number_suffix_roundtrip() { + assert_eq!( + ron::from_str::("1_f32").unwrap(), + ron::Value::Number(ron::value::Number::new(1_f32)) + ); + assert_eq!( + ron::from_str::("-1_f32").unwrap(), + ron::Value::Number(ron::value::Number::new(-1_f32)) + ); + + check_number_roundtrip(f32::NAN, "f32", f64::NAN); + check_number_roundtrip(-f32::NAN, "f32", -f64::NAN); + check_number_roundtrip(f32::INFINITY, "f32", f64::INFINITY); + check_number_roundtrip(f32::NEG_INFINITY, "f32", f64::NEG_INFINITY); + + check_number_roundtrip(f64::NAN, "f64", f64::NAN); + check_number_roundtrip(-f64::NAN, "f64", -f64::NAN); + check_number_roundtrip(f64::INFINITY, "f64", f64::INFINITY); + check_number_roundtrip(f64::NEG_INFINITY, "f64", f64::NEG_INFINITY); + + macro_rules! test_min_max { + ($($ty:ty),*) => { + $( + check_number_roundtrip(<$ty>::MIN, stringify!($ty), <$ty>::MIN as f64); + check_number_roundtrip(<$ty>::MAX, stringify!($ty), <$ty>::MAX as f64); + )* + }; + } + + test_min_max! { i8, i16, i32, i64, u8, u16, u32, u64, f32, f64 } + #[cfg(feature = "integer128")] + test_min_max! { i128, u128 } +} + +fn check_number_roundtrip< + T: Copy + + Into + + serde::Serialize + + serde::de::DeserializeOwned + + PartialEq + + std::fmt::Debug, +>( + n: T, + suffix: &str, + n_f64: f64, +) { + let number: Number = n.into(); + let ron = ron::ser::to_string_pretty( + &number, + ron::ser::PrettyConfig::default().number_suffixes(true), + ) + .unwrap(); + assert!(ron.ends_with(suffix)); + + let ron = + ron::ser::to_string_pretty(&n, ron::ser::PrettyConfig::default().number_suffixes(true)) + .unwrap(); + assert!(ron.ends_with(suffix)); + + let de: ron::Value = ron::from_str(&ron).unwrap(); + assert_eq!(de, ron::Value::Number(number)); + + let de: T = ron::from_str(&ron).unwrap(); + let de_number: Number = de.into(); + assert_eq!(de_number, number); + + assert_eq!(Number::from(de_number.into_f64()), Number::from(n_f64)); +} + +#[test] +fn negative_unsigned() { + assert_eq!( + ron::from_str::("-1u8"), + Err(ron::error::SpannedError { + code: ron::Error::IntegerOutOfBounds, + position: ron::error::Position { line: 1, col: 5 }, + }) + ); + assert_eq!( + ron::from_str::("-1u16"), + Err(ron::error::SpannedError { + code: ron::Error::IntegerOutOfBounds, + position: ron::error::Position { line: 1, col: 6 }, + }) + ); + assert_eq!( + ron::from_str::("-1u32"), + Err(ron::error::SpannedError { + code: ron::Error::IntegerOutOfBounds, + position: ron::error::Position { line: 1, col: 6 }, + }) + ); + assert_eq!( + ron::from_str::("-1u64"), + Err(ron::error::SpannedError { + code: ron::Error::IntegerOutOfBounds, + position: ron::error::Position { line: 1, col: 6 }, + }) + ); + #[cfg(feature = "integer128")] + assert_eq!( + ron::from_str::("-1u128"), + Err(ron::error::SpannedError { + code: ron::Error::IntegerOutOfBounds, + position: ron::error::Position { line: 1, col: 7 }, + }) + ); + + assert_eq!( + ron::from_str::("-1u8"), + Err(ron::error::SpannedError { + code: ron::Error::IntegerOutOfBounds, + position: ron::error::Position { line: 1, col: 5 }, + }) + ); + assert_eq!( + ron::from_str::("-1u16"), + Err(ron::error::SpannedError { + code: ron::Error::IntegerOutOfBounds, + position: ron::error::Position { line: 1, col: 6 }, + }) + ); + assert_eq!( + ron::from_str::("-1u32"), + Err(ron::error::SpannedError { + code: ron::Error::IntegerOutOfBounds, + position: ron::error::Position { line: 1, col: 6 }, + }) + ); + assert_eq!( + ron::from_str::("-1u64"), + Err(ron::error::SpannedError { + code: ron::Error::IntegerOutOfBounds, + position: ron::error::Position { line: 1, col: 6 }, + }) + ); + #[cfg(feature = "integer128")] + assert_eq!( + ron::from_str::("-1u128"), + Err(ron::error::SpannedError { + code: ron::Error::IntegerOutOfBounds, + position: ron::error::Position { line: 1, col: 7 }, + }) + ); +} + +#[test] +fn invalid_suffix() { + assert_eq!( + ron::from_str::("1u7"), + Err(ron::error::SpannedError { + code: ron::Error::TrailingCharacters, + position: ron::error::Position { line: 1, col: 2 }, + }) + ); + assert_eq!( + ron::from_str::("1f17"), + Err(ron::error::SpannedError { + code: ron::Error::TrailingCharacters, + position: ron::error::Position { line: 1, col: 2 }, + }) + ); + #[cfg(not(feature = "integer128"))] + assert_eq!( + ron::from_str::("1u128"), + Err(ron::error::SpannedError { + code: ron::Error::TrailingCharacters, + position: ron::error::Position { line: 1, col: 2 }, + }) + ); + #[cfg(not(feature = "integer128"))] + assert_eq!( + ron::from_str::("1i128"), + Err(ron::error::SpannedError { + code: ron::Error::TrailingCharacters, + position: ron::error::Position { line: 1, col: 2 }, + }) + ); + + assert_eq!( + ron::from_str::("1u7"), + Err(ron::error::SpannedError { + code: ron::Error::TrailingCharacters, + position: ron::error::Position { line: 1, col: 2 }, + }) + ); + assert_eq!( + ron::from_str::("1f17"), + Err(ron::error::SpannedError { + code: ron::Error::TrailingCharacters, + position: ron::error::Position { line: 1, col: 2 }, + }) + ); + #[cfg(not(feature = "integer128"))] + assert_eq!( + ron::from_str::("1u128"), + Err(ron::error::SpannedError { + code: ron::Error::TrailingCharacters, + position: ron::error::Position { line: 1, col: 2 }, + }) + ); + #[cfg(not(feature = "integer128"))] + assert_eq!( + ron::from_str::("1i128"), + Err(ron::error::SpannedError { + code: ron::Error::TrailingCharacters, + position: ron::error::Position { line: 1, col: 2 }, + }) + ); +} + +#[test] +fn number_type_mismatch() { + assert_eq!( + ron::from_str::("1i32"), + Err(ron::error::SpannedError { + code: ron::Error::InvalidValueForType { + expected: String::from("an 8-bit unsigned integer"), + found: String::from("1i32") + }, + position: ron::error::Position { line: 1, col: 5 }, + }) + ); + + assert_eq!( + ron::from_str::("-1u8"), + Err(ron::error::SpannedError { + code: ron::Error::IntegerOutOfBounds, + position: ron::error::Position { line: 1, col: 5 }, + }) + ); + + assert_eq!( + ron::from_str::("1f64"), + Err(ron::error::SpannedError { + code: ron::Error::InvalidValueForType { + expected: String::from("a 32-bit floating point number"), + found: String::from("1f64") + }, + position: ron::error::Position { line: 1, col: 5 }, + }) + ); + + assert_eq!( + ron::from_str::("1f32"), + Err(ron::error::SpannedError { + code: ron::Error::InvalidValueForType { + expected: String::from("a 64-bit floating point number"), + found: String::from("1f32") + }, + position: ron::error::Position { line: 1, col: 5 }, + }) + ); + + macro_rules! test_mismatch { + ($($ty:ty),*) => { + $( + check_number_type_mismatch::<$ty>("i8"); + check_number_type_mismatch::<$ty>("i16"); + check_number_type_mismatch::<$ty>("i32"); + check_number_type_mismatch::<$ty>("i64"); + #[cfg(feature = "integer128")] + check_number_type_mismatch::<$ty>("i128"); + check_number_type_mismatch::<$ty>("u8"); + check_number_type_mismatch::<$ty>("u16"); + check_number_type_mismatch::<$ty>("u32"); + check_number_type_mismatch::<$ty>("u64"); + #[cfg(feature = "integer128")] + check_number_type_mismatch::<$ty>("u128"); + )* + }; + } + + test_mismatch! { i8, i16, i32, i64, u8, u16, u32, u64 } + #[cfg(feature = "integer128")] + test_mismatch! { i128, u128 } +} + +fn check_number_type_mismatch(suffix: &str) { + let ron = format!("0{suffix}"); + + if suffix.starts_with(std::any::type_name::()) { + assert!(ron::from_str::(&ron).is_ok()); + return; + } + + let err = ron::from_str::(&ron).unwrap_err(); + + println!("{:?} {}", err, suffix); + + assert_eq!( + err.position, + ron::error::Position { + line: 1, + col: 2 + suffix.len() + } + ); + + if !matches!(&err.code, ron::Error::InvalidValueForType { found, .. } if found == &ron ) { + panic!("{:?}", err.code); // GRCOV_EXCL_LINE + } +} + +#[test] +fn float_const_prefix() { + assert_eq!( + ron::from_str::("NaNf32a").unwrap_err(), + ron::error::SpannedError { + code: ron::Error::ExpectedFloat, + position: ron::error::Position { line: 1, col: 1 }, + } + ); + + assert_eq!( + ron::from_str::("-inff64a").unwrap_err(), + ron::error::SpannedError { + code: ron::Error::ExpectedFloat, + position: ron::error::Position { line: 1, col: 1 }, + } + ); + + assert_eq!( + ron::from_str::("+NaNf17").unwrap_err(), + ron::error::SpannedError { + code: ron::Error::ExpectedFloat, + position: ron::error::Position { line: 1, col: 1 }, + } + ); +} + +#[test] +fn invalid_float() { + assert_eq!( + ron::from_str::("1ee3").unwrap_err(), + ron::error::SpannedError { + code: ron::Error::ExpectedFloat, + position: ron::error::Position { line: 1, col: 1 }, + } + ); + assert_eq!( + ron::from_str::("1ee3f32").unwrap_err(), + ron::error::SpannedError { + code: ron::Error::ExpectedFloat, + position: ron::error::Position { line: 1, col: 1 }, + } + ); + assert_eq!( + ron::from_str::("1ee3f64").unwrap_err(), + ron::error::SpannedError { + code: ron::Error::ExpectedFloat, + position: ron::error::Position { line: 1, col: 1 }, + } + ); +} + +#[test] +fn fuzzer_found_issues() { + #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq)] + enum A { + #[serde(rename = "true")] + True(bool), + #[serde(rename = "false")] + False(bool), + #[serde(rename = "Some")] + Some(bool), + #[serde(rename = "None")] + None(bool), + #[serde(rename = "inf")] + Inf(bool), + #[serde(rename = "inff32")] + InfF32(bool), + #[serde(rename = "inff64")] + InfF64(bool), + #[serde(rename = "NaN")] + NaN(bool), + #[serde(rename = "NaNf32")] + NaNF32(bool), + #[serde(rename = "NaNf64")] + NaNF64(bool), + } + + assert_eq!(ron::to_string(&A::True(false)).unwrap(), "r#true(false)"); + assert_eq!(ron::from_str::("r#true(false)").unwrap(), A::True(false)); + assert_eq!( + ron::from_str::("true(false)").unwrap_err(), + ron::error::SpannedError { + code: ron::Error::TrailingCharacters, + position: ron::error::Position { line: 1, col: 5 }, + } + ); + + assert_eq!(ron::to_string(&A::False(true)).unwrap(), "r#false(true)"); + assert_eq!(ron::from_str::("r#false(true)").unwrap(), A::False(true)); + assert_eq!( + ron::from_str::("false(true)").unwrap_err(), + ron::error::SpannedError { + code: ron::Error::TrailingCharacters, + position: ron::error::Position { line: 1, col: 6 }, + } + ); + + assert_eq!(ron::to_string(&A::Some(false)).unwrap(), "r#Some(false)"); + assert_eq!(ron::from_str::("r#Some(false)").unwrap(), A::Some(false)); + assert_eq!( + ron::from_str::("Some(false)").unwrap(), + ron::Value::Option(Some(Box::new(ron::Value::Bool(false)))), + ); + + assert_eq!(ron::to_string(&A::None(true)).unwrap(), "r#None(true)"); + assert_eq!(ron::from_str::("r#None(true)").unwrap(), A::None(true)); + assert_eq!( + ron::from_str::("None(true)").unwrap_err(), + ron::error::SpannedError { + code: ron::Error::TrailingCharacters, + position: ron::error::Position { line: 1, col: 5 }, + } + ); + + assert_eq!(ron::to_string(&A::Inf(false)).unwrap(), "r#inf(false)"); + assert_eq!(ron::from_str::("r#inf(false)").unwrap(), A::Inf(false)); + assert_eq!( + ron::from_str::("inf(false)").unwrap_err(), + ron::error::SpannedError { + code: ron::Error::TrailingCharacters, + position: ron::error::Position { line: 1, col: 4 }, + } + ); + + assert_eq!( + ron::to_string(&A::InfF32(false)).unwrap(), + "r#inff32(false)" + ); + assert_eq!( + ron::from_str::("r#inff32(false)").unwrap(), + A::InfF32(false) + ); + assert_eq!( + ron::from_str::("inff32(false)").unwrap_err(), + ron::error::SpannedError { + code: ron::Error::TrailingCharacters, + position: ron::error::Position { line: 1, col: 7 }, + } + ); + + assert_eq!( + ron::to_string(&A::InfF64(false)).unwrap(), + "r#inff64(false)" + ); + assert_eq!( + ron::from_str::("r#inff64(false)").unwrap(), + A::InfF64(false) + ); + assert_eq!( + ron::from_str::("inff64(false)").unwrap_err(), + ron::error::SpannedError { + code: ron::Error::TrailingCharacters, + position: ron::error::Position { line: 1, col: 7 }, + } + ); + + assert_eq!(ron::to_string(&A::NaN(true)).unwrap(), "r#NaN(true)"); + assert_eq!(ron::from_str::("r#NaN(true)").unwrap(), A::NaN(true)); + assert_eq!( + ron::from_str::("NaN(true)").unwrap_err(), + ron::error::SpannedError { + code: ron::Error::TrailingCharacters, + position: ron::error::Position { line: 1, col: 4 }, + } + ); + + assert_eq!(ron::to_string(&A::NaNF32(true)).unwrap(), "r#NaNf32(true)"); + assert_eq!( + ron::from_str::("r#NaNf32(true)").unwrap(), + A::NaNF32(true) + ); + assert_eq!( + ron::from_str::("NaNf32(true)").unwrap_err(), + ron::error::SpannedError { + code: ron::Error::TrailingCharacters, + position: ron::error::Position { line: 1, col: 7 }, + } + ); + + assert_eq!(ron::to_string(&A::NaNF64(true)).unwrap(), "r#NaNf64(true)"); + assert_eq!( + ron::from_str::("r#NaNf64(true)").unwrap(), + A::NaNF64(true) + ); + assert_eq!( + ron::from_str::("NaNf64(true)").unwrap_err(), + ron::error::SpannedError { + code: ron::Error::TrailingCharacters, + position: ron::error::Position { line: 1, col: 7 }, + } + ); +} diff --git a/third_party/rust/ron/tests/492_enum_in_untagged_enum.rs b/third_party/rust/ron/tests/492_enum_in_untagged_enum.rs new file mode 100644 index 000000000000..1b3bf52771f9 --- /dev/null +++ b/third_party/rust/ron/tests/492_enum_in_untagged_enum.rs @@ -0,0 +1,33 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +enum Other { + Env(String), +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[serde(untagged)] +enum MaybeEnv { + Value(String), + Other(Other), +} + +#[test] +fn enum_in_untagged_enum() { + check_roundtrip(&MaybeEnv::Value(String::from("foo")), "\"foo\""); + check_roundtrip( + &MaybeEnv::Other(Other::Env(String::from("bar"))), + "Env(\"bar\")", + ); +} + +fn check_roundtrip( + val: &T, + check: &str, +) { + let ron = ron::to_string(&val).unwrap(); + assert_eq!(ron, check); + + let de = ron::from_str::(&ron).unwrap(); + assert_eq!(&de, val); +} diff --git a/third_party/rust/ron/tests/502_known_bugs.rs b/third_party/rust/ron/tests/502_known_bugs.rs new file mode 100644 index 000000000000..2cd0b621a665 --- /dev/null +++ b/third_party/rust/ron/tests/502_known_bugs.rs @@ -0,0 +1,3078 @@ +use std::collections::{BTreeMap, HashMap}; + +use ron::{error::Position, error::SpannedError, extensions::Extensions, ser::PrettyConfig, Error}; +use serde::{Deserialize, Serialize}; + +#[test] +fn struct_names_inside_internally_tagged() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: i32, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag")] + enum InternallyTagged { + B { ho: i32, a: A }, + } + + assert_eq!( + check_roundtrip( + &InternallyTagged::B { + ho: 24, + a: A { hi: 42 } + }, + PrettyConfig::default() + ), + Ok(()) + ); + assert_eq!( + check_roundtrip( + &InternallyTagged::B { + ho: 24, + a: A { hi: 42 } + }, + PrettyConfig::default().struct_names(true) + ), + Err(Err(SpannedError { + code: Error::MissingStructField { + field: "hi", + outer: None + }, + position: Position { line: 7, col: 2 } + })), + ); +} + +#[test] +fn struct_names_inside_adjacently_tagged() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: i32, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag", content = "content")] + enum AdjacentlyTagged { + B { ho: i32, a: A }, + } + + assert_eq!( + check_roundtrip( + &AdjacentlyTagged::B { + ho: 24, + a: A { hi: 42 } + }, + PrettyConfig::default() + ), + Ok(()) + ); + assert_eq!( + check_roundtrip( + &AdjacentlyTagged::B { + ho: 24, + a: A { hi: 42 } + }, + PrettyConfig::default().struct_names(true) + ), + Ok(()), + ); + assert_eq!( + ron::from_str::( + "AdjacentlyTagged(tag: B, content: B(ho: 24, a: A(hi: 42)))" + ), + Ok(AdjacentlyTagged::B { + ho: 24, + a: A { hi: 42 } + }), + ); + assert_eq!( + ron::from_str::( + "AdjacentlyTagged(content: B(ho: 24, a: A(hi: 42)), tag: B)" + ), + Err(SpannedError { + code: Error::MissingStructField { + field: "ho", + outer: Some(String::from("AdjacentlyTagged")) + }, + position: Position { line: 1, col: 58 } + }), + ); +} + +#[test] +fn struct_names_inside_untagged() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: i32, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(untagged)] + enum Untagged { + B { ho: i32, a: A }, + } + + assert_eq!( + check_roundtrip( + &Untagged::B { + ho: 24, + a: A { hi: 42 } + }, + PrettyConfig::default() + ), + Ok(()) + ); + assert_eq!( + check_roundtrip( + &Untagged::B { + ho: 24, + a: A { hi: 42 } + }, + PrettyConfig::default().struct_names(true) + ), + Err(Err(SpannedError { + code: Error::Message(String::from( + "data did not match any variant of untagged enum Untagged" + )), + position: Position { line: 6, col: 2 } + })), + ); +} + +#[test] +fn struct_names_inside_flatten_struct() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: i32, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct B { + a: A, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct FlattenedStruct { + ho: i32, + #[serde(flatten)] + a: B, + } + + assert_eq!( + check_roundtrip( + &FlattenedStruct { + ho: 24, + a: B { a: A { hi: 42 } } + }, + PrettyConfig::default() + ), + Ok(()) + ); + assert_eq!( + check_roundtrip( + &FlattenedStruct { + ho: 24, + a: B { a: A { hi: 42 } } + }, + PrettyConfig::default().struct_names(true) + ), + Err(Err(SpannedError { + code: Error::MissingStructField { + field: "hi", + outer: None + }, + position: Position { line: 6, col: 1 } + })), + ); +} + +#[test] +fn struct_names_inside_flatten_struct_variant() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: i32, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct B { + a: A, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum FlattenedStructVariant { + C { + ho: i32, + #[serde(flatten)] + a: B, + }, + } + + assert_eq!( + check_roundtrip( + &FlattenedStructVariant::C { + ho: 24, + a: B { a: A { hi: 42 } } + }, + PrettyConfig::default() + ), + Ok(()) + ); + assert_eq!( + check_roundtrip( + &FlattenedStructVariant::C { + ho: 24, + a: B { a: A { hi: 42 } } + }, + PrettyConfig::default().struct_names(true) + ), + Err(Err(SpannedError { + code: Error::MissingStructField { + field: "hi", + outer: Some(String::from("C")) + }, + position: Position { line: 6, col: 1 } + })), + ); +} + +#[test] +fn implicit_some_inside_internally_tagged() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: Option>, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag")] + enum InternallyTagged { + B { ho: i32, a: A }, + } + + assert_eq!( + check_roundtrip( + &InternallyTagged::B { + ho: 24, + a: A { hi: Some(Some(())) } + }, + PrettyConfig::default() + ), + Ok(()) + ); + assert_eq!( + check_roundtrip( + &InternallyTagged::B { + ho: 24, + a: A { hi: Some(Some(())) } + }, + PrettyConfig::default().extensions(Extensions::IMPLICIT_SOME) + ), + Err(Ok(Error::Message(String::from("ROUNDTRIP error: B { ho: 24, a: A { hi: Some(Some(())) } } != B { ho: 24, a: A { hi: None } }")))) + ); +} + +#[test] +fn implicit_some_inside_adjacently_tagged() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct Unit; + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: Option>, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag", content = "content")] + enum AdjacentlyTagged { + B { ho: i32, a: A }, + } + + assert_eq!( + check_roundtrip( + &AdjacentlyTagged::B { + ho: 24, + a: A { + hi: Some(Some(Unit)) + } + }, + PrettyConfig::default() + ), + Ok(()) + ); + assert_eq!( + check_roundtrip( + &AdjacentlyTagged::B { + ho: 24, + a: A { + hi: Some(Some(Unit)) + } + }, + PrettyConfig::default().extensions(Extensions::IMPLICIT_SOME) + ), + Ok(()), + ); + assert_eq!( + ron::from_str::( + "#![enable(implicit_some)] (tag: B, content: (ho: 24, a: A(hi: ())))" + ), + Ok(AdjacentlyTagged::B { + ho: 24, + a: A { + hi: Some(Some(Unit)) + } + }), + ); + assert_eq!( + ron::from_str::( + "#![enable(implicit_some)] (content: (ho: 24, a: A(hi: ())), tag: B)" + ), + Ok(AdjacentlyTagged::B { + ho: 24, + a: A { hi: None } // THIS IS WRONG + }), + ); +} + +#[test] +fn implicit_some_inside_untagged() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: Option>, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(untagged)] + enum Untagged { + B { ho: i32, a: A }, + } + + assert_eq!( + check_roundtrip( + &Untagged::B { + ho: 24, + a: A { hi: Some(Some(())) } + }, + PrettyConfig::default() + ), + Ok(()) + ); + assert_eq!( + check_roundtrip( + &Untagged::B { + ho: 24, + a: A { hi: Some(Some(())) } + }, + PrettyConfig::default().extensions(Extensions::IMPLICIT_SOME) + ), + Err(Ok(Error::Message(String::from( + "ROUNDTRIP error: B { ho: 24, a: A { hi: Some(Some(())) } } != B { ho: 24, a: A { hi: None } }" + )))), + ); +} + +#[test] +fn implicit_some_inside_flatten_struct() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(untagged)] + enum Untagged { + Unit, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: Option>, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct B { + a: A, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct FlattenedStruct { + ho: i32, + #[serde(flatten)] + a: B, + } + + assert_eq!( + check_roundtrip( + &FlattenedStruct { + ho: 24, + a: B { + a: A { + hi: Some(Some(Untagged::Unit)) + } + } + }, + PrettyConfig::default() + ), + Ok(()) + ); + assert_eq!( + check_roundtrip( + &FlattenedStruct { + ho: 24, + a: B { + a: A { hi: Some(Some(Untagged::Unit)) } + } + }, + PrettyConfig::default().extensions(Extensions::IMPLICIT_SOME) + ), + Err(Ok(Error::Message(String::from("ROUNDTRIP error: FlattenedStruct { ho: 24, a: B { a: A { hi: Some(Some(Unit)) } } } != FlattenedStruct { ho: 24, a: B { a: A { hi: None } } }")))) + ); +} + +#[test] +fn implicit_some_inside_flatten_struct_variant() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: Option>, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct B { + a: A, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum FlattenedStructVariant { + C { + ho: i32, + #[serde(flatten)] + a: B, + }, + } + + assert_eq!( + check_roundtrip( + &FlattenedStructVariant::C { + ho: 24, + a: B { + a: A { hi: Some(Some([])) } + } + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("an empty array"), + found: String::from("a unit value") + }, + position: Position { line: 6, col: 1 } + })) + ); + assert_eq!( + check_roundtrip( + &FlattenedStructVariant::C { + ho: 24, + a: B { + a: A { hi: Some(Some([])) } + } + }, + PrettyConfig::default().extensions(Extensions::IMPLICIT_SOME) + ), + Err(Ok(Error::Message(String::from("ROUNDTRIP error: C { ho: 24, a: B { a: A { hi: Some(Some([])) } } } != C { ho: 24, a: B { a: A { hi: None } } }")))) + ); +} + +#[test] +fn newtype_inside_internally_tagged() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A(i32); + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag")] + enum InternallyTagged { + B { ho: i32, a: A }, + } + + // NOTE: + // 1. ron is correctly collected into Content, newtype is a one-seq here + // 2. newtype asks ContentDeserializer for newtype + // 3. ContentDeserializer forwards any value to visit_newtype_struct + // https://github.com/serde-rs/serde/blob/8c4aad3a59515f7b779f764d5e16d6bae297ab7f/serde/src/private/de.rs#L1347-L1359 + + assert_eq!( + check_roundtrip( + &InternallyTagged::B { ho: 24, a: A(42) }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("i32"), + found: String::from("a sequence") + }, + position: Position { line: 5, col: 2 } + })) + ); +} + +#[test] +fn newtype_inside_adjacently_tagged() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A(i32); + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag", content = "content")] + enum AdjacentlyTagged { + B { ho: i32, a: A }, + } + + assert_eq!( + check_roundtrip( + &AdjacentlyTagged::B { ho: 24, a: A(42) }, + PrettyConfig::default() + ), + Ok(()) + ); + assert_eq!( + ron::from_str::("(tag: B, content: (ho: 24, a: (42)))"), + Ok(AdjacentlyTagged::B { ho: 24, a: A(42) }), + ); + assert_eq!( + ron::from_str::("(content: (ho: 24, a: (42)), tag: B)"), + Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("i32"), + found: String::from("a sequence") + }, + position: Position { line: 1, col: 36 } + }) + ); +} + +#[test] +fn newtype_inside_untagged() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A(i32); + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(untagged)] + enum Untagged { + B { ho: i32, a: A }, + } + + assert_eq!( + check_roundtrip(&Untagged::B { ho: 24, a: A(42) }, PrettyConfig::default()), + Err(Err(SpannedError { + code: Error::Message(String::from( + "data did not match any variant of untagged enum Untagged" + )), + position: Position { line: 4, col: 2 } + })) + ); +} + +#[test] +fn newtype_inside_flatten_struct() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A(i32); + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct B { + a: A, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct FlattenedStruct { + ho: i32, + #[serde(flatten)] + a: B, + } + + assert_eq!( + check_roundtrip( + &FlattenedStruct { + ho: 24, + a: B { a: A(42) } + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("i32"), + found: String::from("a sequence") + }, + position: Position { line: 4, col: 1 } + })) + ); +} + +#[test] +fn newtype_inside_flatten_struct_variant() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A(i32); + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct B { + a: A, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum FlattenedStructVariant { + C { + ho: i32, + #[serde(flatten)] + a: B, + }, + } + + assert_eq!( + check_roundtrip( + &FlattenedStructVariant::C { + ho: 24, + a: B { a: A(42) } + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("i32"), + found: String::from("a sequence") + }, + position: Position { line: 4, col: 1 } + })) + ); +} + +#[test] +fn one_tuple_inside_unwrapped_newtype_variant_inside_internally_tagged() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum A { + Newtype((i32,)), + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag")] + enum InternallyTagged { + B { ho: i32, a: A }, + } + + assert_eq!( + check_roundtrip( + &InternallyTagged::B { + ho: 24, + a: A::Newtype((42,)) + }, + PrettyConfig::default() + ), + Ok(()) + ); + assert_eq!( + check_roundtrip( + &InternallyTagged::B { + ho: 24, + a: A::Newtype((42,)) + }, + PrettyConfig::default().extensions(Extensions::UNWRAP_VARIANT_NEWTYPES) + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("a tuple of size 1"), + found: String::from("the unsigned integer `42`") + }, + position: Position { line: 6, col: 2 } + })) + ); +} + +#[test] +fn one_tuple_inside_unwrapped_newtype_variant_inside_adjacently_tagged() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum A { + Newtype([i32; 1]), + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag", content = "content")] + enum AdjacentlyTagged { + B { ho: i32, a: A }, + } + + assert_eq!( + check_roundtrip( + &AdjacentlyTagged::B { + ho: 24, + a: A::Newtype([42]) + }, + PrettyConfig::default() + ), + Ok(()) + ); + assert_eq!( + check_roundtrip( + &AdjacentlyTagged::B { + ho: 24, + a: A::Newtype([42]) + }, + PrettyConfig::default().extensions(Extensions::UNWRAP_VARIANT_NEWTYPES) + ), + Ok(()) + ); + assert_eq!( + ron::from_str::( + "#![enable(unwrap_variant_newtypes)] (tag: B, content: (ho: 24, a: Newtype(42)))" + ), + Ok(AdjacentlyTagged::B { + ho: 24, + a: A::Newtype([42]) + }), + ); + assert_eq!( + ron::from_str::( + "#![enable(unwrap_variant_newtypes)] (content: (ho: 24, a: Newtype(42)), tag: B)" + ), + Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("an array of length 1"), + found: String::from("the unsigned integer `42`") + }, + position: Position { line: 1, col: 79 } + }) + ); +} + +#[test] +fn one_tuple_inside_unwrapped_newtype_variant_inside_untagged() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct OneTuple(i32, #[serde(skip)] ()); + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(untagged)] + enum Untagged { + B { ho: i32, a: Option }, + } + + assert_eq!( + check_roundtrip( + &Untagged::B { + ho: 24, + a: Some(OneTuple(42, ())) + }, + PrettyConfig::default() + ), + Ok(()) + ); + assert_eq!( + check_roundtrip( + &Untagged::B { + ho: 24, + a: Some(OneTuple(42, ())) + }, + PrettyConfig::default().extensions(Extensions::UNWRAP_VARIANT_NEWTYPES) + ), + Err(Err(SpannedError { + code: Error::Message(String::from( + "data did not match any variant of untagged enum Untagged" + )), + position: Position { line: 5, col: 2 } + })) + ); +} + +#[test] +fn one_tuple_inside_unwrapped_newtype_variant_inside_flatten_struct() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum A { + Newtype([i32; 1]), + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct B { + a: A, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct FlattenedStruct { + ho: i32, + #[serde(flatten)] + a: B, + } + + assert_eq!( + check_roundtrip( + &FlattenedStruct { + ho: 24, + a: B { + a: A::Newtype([42]) + } + }, + PrettyConfig::default() + ), + Ok(()) + ); + assert_eq!( + check_roundtrip( + &FlattenedStruct { + ho: 24, + a: B { + a: A::Newtype([42]) + } + }, + PrettyConfig::default().extensions(Extensions::UNWRAP_VARIANT_NEWTYPES) + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("an array of length 1"), + found: String::from("the unsigned integer `42`") + }, + position: Position { line: 5, col: 1 } + })) + ); +} + +#[test] +fn one_tuple_inside_unwrapped_newtype_variant_inside_flatten_struct_variant() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum A { + Newtype((i32,)), + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct B { + a: A, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum FlattenedStructVariant { + C { + ho: i32, + #[serde(flatten)] + a: B, + }, + } + + assert_eq!( + check_roundtrip( + &FlattenedStructVariant::C { + ho: 24, + a: B { + a: A::Newtype((42,)) + } + }, + PrettyConfig::default() + ), + Ok(()), + ); + assert_eq!( + check_roundtrip( + &FlattenedStructVariant::C { + ho: 24, + a: B { + a: A::Newtype((42,)) + } + }, + PrettyConfig::default().extensions(Extensions::UNWRAP_VARIANT_NEWTYPES) + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("a tuple of size 1"), + found: String::from("the unsigned integer `42`") + }, + position: Position { line: 5, col: 1 } + })) + ); +} + +#[test] +fn one_tuple_variant_inside_internally_tagged() { + // A tuple variant with just one element that is not a newtype variant + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum OneEnum { + OneTuple(i32, #[serde(skip)] ()), + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: OneEnum, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag")] + enum InternallyTagged { + B { ho: i32, a: A }, + } + + assert_eq!( + check_roundtrip( + &InternallyTagged::B { + ho: 24, + a: A { + hi: OneEnum::OneTuple(42, ()) + } + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("tuple variant"), + found: String::from("the unsigned integer `42`") + }, + position: Position { line: 7, col: 2 } + })) + ); +} + +#[test] +fn one_tuple_variant_inside_adjacently_tagged() { + // A tuple variant with just one element that is not a newtype variant + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum OneEnum { + OneTuple(i32, #[serde(skip)] ()), + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: OneEnum, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag", content = "content")] + enum AdjacentlyTagged { + B { ho: i32, a: A }, + } + + assert_eq!( + check_roundtrip( + &AdjacentlyTagged::B { + ho: 24, + a: A { + hi: OneEnum::OneTuple(42, ()) + } + }, + PrettyConfig::default() + ), + Ok(()) + ); + assert_eq!( + ron::from_str::("(tag: B, content: (ho: 24, a: (hi: OneTuple(42))))"), + Ok(AdjacentlyTagged::B { + ho: 24, + a: A { + hi: OneEnum::OneTuple(42, ()) + } + }), + ); + assert_eq!( + ron::from_str::("(content: (ho: 24, a: (hi: OneTuple(42))), tag: B)"), + Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("tuple variant"), + found: String::from("the unsigned integer `42`") + }, + position: Position { line: 1, col: 50 } + }) + ); +} + +#[test] +fn one_tuple_variant_inside_untagged() { + // A tuple variant with just one element that is not a newtype variant + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum OneEnum { + OneTuple(i32, #[serde(skip)] ()), + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: OneEnum, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(untagged)] + enum Untagged { + B { ho: i32, a: A }, + } + + assert_eq!( + check_roundtrip( + &Untagged::B { + ho: 24, + a: A { + hi: OneEnum::OneTuple(42, ()) + } + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::Message(String::from( + "data did not match any variant of untagged enum Untagged" + )), + position: Position { line: 6, col: 2 } + })) + ); +} + +#[test] +fn one_tuple_variant_inside_flatten_struct() { + // A tuple variant with just one element that is not a newtype variant + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum OneEnum { + OneTuple(i32, #[serde(skip)] ()), + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: OneEnum, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct B { + a: A, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct FlattenedStruct { + ho: i32, + #[serde(flatten)] + a: B, + } + + assert_eq!( + check_roundtrip( + &FlattenedStruct { + ho: 24, + a: B { + a: A { + hi: OneEnum::OneTuple(42, ()) + } + } + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("tuple variant"), + found: String::from("the unsigned integer `42`") + }, + position: Position { line: 6, col: 1 } + })) + ); +} + +#[test] +fn one_tuple_variant_inside_flatten_struct_variant() { + // A tuple variant with just one element that is not a newtype variant + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum OneEnum { + OneTuple(i32, #[serde(skip)] ()), + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: OneEnum, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct B { + a: A, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum FlattenedStructVariant { + C { + ho: i32, + #[serde(flatten)] + a: B, + }, + } + + assert_eq!( + check_roundtrip( + &FlattenedStructVariant::C { + ho: 24, + a: B { + a: A { + hi: OneEnum::OneTuple(42, ()) + } + } + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("tuple variant"), + found: String::from("the unsigned integer `42`") + }, + position: Position { line: 6, col: 1 } + })) + ); +} + +#[test] +fn raw_value_inside_internally_tagged() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: Box, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag")] + enum InternallyTagged { + B { ho: i32, a: A }, + } + + assert_eq!( + check_roundtrip( + &InternallyTagged::B { + ho: 24, + a: A { + hi: ron::value::RawValue::from_boxed_ron(String::from("42").into_boxed_str()) + .unwrap(), + } + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("any valid RON-value-string"), + found: String::from("the unsigned integer `42`") + }, + position: Position { line: 7, col: 2 } + })) + ); +} + +#[test] +fn raw_value_inside_adjacently_tagged() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: Box, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag", content = "content")] + enum AdjacentlyTagged { + B { ho: i32, a: A }, + } + + assert_eq!( + check_roundtrip( + &AdjacentlyTagged::B { + ho: 24, + a: A { + hi: ron::value::RawValue::from_boxed_ron(String::from("42").into_boxed_str()) + .unwrap(), + } + }, + PrettyConfig::default() + ), + // adds an extra space + Err(Ok(Error::Message(String::from("ROUNDTRIP error: B { ho: 24, a: A { hi: RawValue(42) } } != B { ho: 24, a: A { hi: RawValue( 42) } }")))) + ); + assert_eq!( + ron::from_str::("(tag: B, content: (ho: 24, a: (hi:42)))"), + Ok(AdjacentlyTagged::B { + ho: 24, + a: A { + hi: ron::value::RawValue::from_boxed_ron(String::from("42").into_boxed_str()) + .unwrap(), + } + }), + ); + assert_eq!( + ron::from_str::("(content: (ho: 24, a: (hi:42)), tag: B)"), + Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("any valid RON-value-string"), + found: String::from("the unsigned integer `42`") + }, + position: Position { line: 1, col: 39 } + }) + ); +} + +#[test] +fn raw_value_inside_untagged() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: Box, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(untagged)] + enum Untagged { + B { ho: i32, a: A }, + } + + assert_eq!( + check_roundtrip( + &Untagged::B { + ho: 24, + a: A { + hi: ron::value::RawValue::from_boxed_ron(String::from("42").into_boxed_str()) + .unwrap(), + } + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::Message(String::from( + "data did not match any variant of untagged enum Untagged" + )), + position: Position { line: 6, col: 2 } + })) + ); +} + +#[test] +fn raw_value_inside_flatten_struct() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: Box, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct B { + a: A, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct FlattenedStruct { + ho: i32, + #[serde(flatten)] + a: B, + } + + assert_eq!( + check_roundtrip( + &FlattenedStruct { + ho: 24, + a: B { + a: A { + hi: ron::value::RawValue::from_boxed_ron( + String::from("42").into_boxed_str() + ) + .unwrap(), + } + } + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("any valid RON-value-string"), + found: String::from("the unsigned integer `42`") + }, + position: Position { line: 6, col: 1 } + })) + ); +} + +#[test] +fn raw_value_inside_flatten_struct_variant() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: Box, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct B { + a: A, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum FlattenedStructVariant { + C { + ho: i32, + #[serde(flatten)] + a: B, + }, + } + + assert_eq!( + check_roundtrip( + &FlattenedStructVariant::C { + ho: 24, + a: B { + a: A { + hi: ron::value::RawValue::from_boxed_ron( + String::from("42").into_boxed_str() + ) + .unwrap(), + } + } + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("any valid RON-value-string"), + found: String::from("the unsigned integer `42`") + }, + position: Position { line: 6, col: 1 } + })) + ); +} + +#[test] +fn unit_like_zero_length_inside_internally_tagged() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: [i32; 0], + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag")] + enum InternallyTagged { + B { ho: i32, a: A }, + } + + assert_eq!( + check_roundtrip( + &InternallyTagged::B { + ho: 24, + a: A { hi: [] } + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("an empty array"), + found: String::from("a unit value") + }, + position: Position { line: 7, col: 2 } + })) + ); +} + +#[test] +fn unit_like_zero_length_inside_adjacently_tagged() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct TupleStruct(); + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: TupleStruct, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag", content = "content")] + enum AdjacentlyTagged { + B { ho: i32, a: A }, + } + + assert_eq!( + check_roundtrip( + &AdjacentlyTagged::B { + ho: 24, + a: A { hi: TupleStruct() } + }, + PrettyConfig::default() + ), + Ok(()), + ); + assert_eq!( + ron::from_str::("(tag: B, content: (ho: 24, a: (hi: ())))"), + Ok(AdjacentlyTagged::B { + ho: 24, + a: A { hi: TupleStruct() } + }), + ); + assert_eq!( + ron::from_str::("(content: (ho: 24, a: (hi: ())), tag: B)"), + Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("tuple struct TupleStruct"), + found: String::from("a unit value") + }, + position: Position { line: 1, col: 40 } + }) + ); +} + +#[test] +fn unit_like_zero_length_inside_untagged() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct Struct {} + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: Struct, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(untagged)] + enum Untagged { + B { ho: i32, a: A }, + } + + assert_eq!( + check_roundtrip( + &Untagged::B { + ho: 24, + a: A { hi: Struct {} } + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::Message(String::from( + "data did not match any variant of untagged enum Untagged" + )), + position: Position { line: 6, col: 2 } + })) + ); +} + +#[test] +fn unit_like_zero_length_inside_flatten_struct() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum Enum { + Tuple(), + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: Enum, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct B { + a: A, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct FlattenedStruct { + ho: i32, + #[serde(flatten)] + a: B, + } + + assert_eq!( + check_roundtrip( + &FlattenedStruct { + ho: 24, + a: B { + a: A { hi: Enum::Tuple() } + } + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("tuple variant"), + found: String::from("a unit value") + }, + position: Position { line: 6, col: 1 } + })) + ); +} + +#[test] +fn unit_like_zero_length_inside_flatten_struct_variant() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum Enum { + Struct {}, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: Enum, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct B { + a: A, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum FlattenedStructVariant { + C { + ho: i32, + #[serde(flatten)] + a: B, + }, + } + + assert_eq!( + check_roundtrip( + &FlattenedStructVariant::C { + ho: 24, + a: B { + a: A { + hi: Enum::Struct {} + } + } + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("struct variant"), + found: String::from("a unit value") + }, + position: Position { line: 6, col: 1 } + })) + ); +} + +#[test] +fn i128_inside_internally_tagged() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: i128, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag")] + enum InternallyTagged { + B { ho: i32, a: A }, + } + + #[cfg(not(feature = "integer128"))] + assert_eq!( + check_roundtrip( + &InternallyTagged::B { + ho: 24, + a: A { hi: i128::MAX } + }, + PrettyConfig::default() + ), + Err(Ok(Error::Message(String::from("i128 is not supported")))) + ); + #[cfg(feature = "integer128")] + assert_eq!( + check_roundtrip( + &InternallyTagged::B { + ho: 24, + a: A { hi: i128::MAX } + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("any value"), + found: format!("integer `{}` as u128", i128::MAX) + }, + position: Position { line: 5, col: 52 } + })) + ); +} + +#[test] +fn u128_inside_adjacently_tagged() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: u128, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag", content = "content")] + enum AdjacentlyTagged { + B { ho: i32, a: A }, + } + + #[cfg(not(feature = "integer128"))] + assert_eq!( + check_roundtrip( + &AdjacentlyTagged::B { + ho: 24, + a: A { hi: u128::MAX } + }, + PrettyConfig::default() + ), + Err(Ok(Error::Message(String::from("u128 is not supported")))) + ); + #[cfg(feature = "integer128")] + assert_eq!( + check_roundtrip( + &AdjacentlyTagged::B { + ho: 24, + a: A { hi: u128::MAX } + }, + PrettyConfig::default() + ), + Ok(()), + ); + #[cfg(feature = "integer128")] + assert_eq!( + ron::from_str::(&format!( + "(tag: B, content: (ho: 24, a: (hi: {})))", + u128::MAX + ),), + Ok(AdjacentlyTagged::B { + ho: 24, + a: A { hi: u128::MAX } + }), + ); + #[cfg(feature = "integer128")] + assert_eq!( + ron::from_str::(&format!( + "(content: (ho: 24, a: (hi: {})), tag: B)", + u128::MAX + ),), + Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("any value"), + found: format!("integer `{}` as u128", u128::MAX) + }, + position: Position { line: 1, col: 67 } + }), + ); +} + +#[test] +fn i128_inside_untagged() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: i128, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(untagged)] + enum Untagged { + B { ho: i32, a: A }, + } + + #[cfg(not(feature = "integer128"))] + assert_eq!( + check_roundtrip( + &Untagged::B { + ho: 24, + a: A { hi: i128::MIN } + }, + PrettyConfig::default() + ), + Err(Ok(Error::Message(String::from("i128 is not supported")))) + ); + #[cfg(feature = "integer128")] + assert_eq!( + check_roundtrip( + &Untagged::B { + ho: 24, + a: A { hi: i128::MIN } + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("any value"), + found: format!("integer `{}` as i128", i128::MIN) + }, + position: Position { line: 4, col: 53 } + })) + ); +} + +#[test] +fn u128_inside_flatten_struct() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: u128, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct B { + a: A, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct FlattenedStruct { + ho: i32, + #[serde(flatten)] + a: B, + } + + #[cfg(not(feature = "integer128"))] + assert_eq!( + check_roundtrip( + &FlattenedStruct { + ho: 24, + a: B { + a: A { hi: u128::MAX } + } + }, + PrettyConfig::default() + ), + Err(Ok(Error::Message(String::from("u128 is not supported")))) + ); + #[cfg(feature = "integer128")] + assert_eq!( + check_roundtrip( + &FlattenedStruct { + ho: 24, + a: B { + a: A { hi: u128::MAX } + } + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("any value"), + found: format!("integer `{}` as u128", u128::MAX) + }, + position: Position { line: 4, col: 52 } + })) + ); +} + +#[test] +fn i128_inside_flatten_struct_variant() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct A { + hi: i128, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct B { + a: A, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum FlattenedStructVariant { + C { + ho: i32, + #[serde(flatten)] + a: B, + }, + } + + #[cfg(not(feature = "integer128"))] + assert_eq!( + check_roundtrip( + &FlattenedStructVariant::C { + ho: 24, + a: B { + a: A { hi: i128::MIN } + } + }, + PrettyConfig::default() + ), + Err(Ok(Error::Message(String::from("i128 is not supported")))) + ); + #[cfg(feature = "integer128")] + assert_eq!( + check_roundtrip( + &FlattenedStructVariant::C { + ho: 24, + a: B { + a: A { hi: i128::MIN } + } + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("any value"), + found: format!("integer `{}` as i128", i128::MIN) + }, + position: Position { line: 4, col: 53 } + })) + ); +} + +#[test] +fn duplicate_key_inside_flatten_struct() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct FlattenedStruct { + ho: i32, + #[serde(flatten)] + other: HashMap, + } + + assert_eq!( + check_roundtrip( + &FlattenedStruct { + ho: 24, + other: [(String::from("ho"), 42)].into_iter().collect(), + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::DuplicateStructField { + field: "ho", + outer: None + }, + position: Position { line: 3, col: 9 } + })) + ); +} + +#[test] +fn duplicate_key_inside_flatten_struct_variant() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct C { + ho: i32, + hi: bool, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct B { + #[serde(flatten)] + inner: C, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum FlattenedStructVariant { + A { + ho: i32, + #[serde(flatten)] + inner: B, + }, + } + + assert_eq!( + check_roundtrip( + &FlattenedStructVariant::A { + ho: 24, + inner: B { + inner: C { ho: 42, hi: false } + } + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::DuplicateStructField { + field: "ho", + outer: Some(String::from("A")) + }, + position: Position { line: 3, col: 9 } + })) + ); +} + +#[test] +fn non_string_key_inside_flatten_struct() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct FlattenedStruct { + ho: i32, + #[serde(flatten)] + other: HashMap, + } + + assert_eq!( + check_roundtrip( + &FlattenedStruct { + ho: 24, + other: [(1, true), (0, false)].into_iter().collect(), + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::ExpectedString, + position: Position { line: 3, col: 5 } + })) + ); +} + +#[test] +fn non_string_key_inside_flatten_struct_variant() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum FlattenedStructVariant { + A { + ho: i32, + #[serde(flatten)] + other: HashMap, + }, + } + + assert_eq!( + check_roundtrip( + &FlattenedStructVariant::A { + ho: 24, + other: [('h', 0), ('i', 1)].into_iter().collect(), + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::ExpectedString, + position: Position { line: 3, col: 5 } + })) + ); +} + +#[test] +fn more_than_one_flatten_map_inside_flatten_struct() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct Deep { + #[serde(flatten)] + other: HashMap, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct Inner { + #[serde(flatten)] + deep: Deep, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct FlattenedStruct { + ho: i32, + #[serde(flatten)] + hi: Inner, + #[serde(flatten)] + other: HashMap, + } + + assert_eq!( + check_roundtrip( + &FlattenedStruct { + ho: 24, + hi: Inner { + deep: Deep { + other: HashMap::new(), + }, + }, + other: [(String::from("42"), true)].into_iter().collect(), + }, + PrettyConfig::default() + ), + // both maps collect all the unknown keys + Err(Ok(Error::Message(String::from("ROUNDTRIP error: FlattenedStruct { ho: 24, hi: Inner { deep: Deep { other: {} } }, other: {\"42\": true} } != FlattenedStruct { ho: 24, hi: Inner { deep: Deep { other: {\"42\": true} } }, other: {\"42\": true} }")))) + ); +} + +#[test] +fn more_than_one_flatten_map_inside_flatten_struct_variant() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct Deep { + #[serde(flatten)] + other: HashMap, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct Inner { + #[serde(flatten)] + deep: Deep, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum FlattenedStructVariant { + A { + ho: i32, + #[serde(flatten)] + hi: Inner, + #[serde(flatten)] + other: HashMap, + }, + } + + assert_eq!( + check_roundtrip( + &FlattenedStructVariant::A { + ho: 24, + hi: Inner { + deep: Deep { + other: [(String::from("24"), false)].into_iter().collect(), + }, + }, + other: HashMap::new(), + }, + PrettyConfig::default() + ), + // both maps collect all the unknown keys + Err(Ok(Error::Message(String::from("ROUNDTRIP error: A { ho: 24, hi: Inner { deep: Deep { other: {\"24\": false} } }, other: {} } != A { ho: 24, hi: Inner { deep: Deep { other: {\"24\": false} } }, other: {\"24\": false} }")))) + ); +} + +#[test] +fn flatten_struct_beside_map_inside_flatten_struct() { + // The non-flattened struct must contain some flattened fields + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct Flattened { + hi: i32, + #[serde(flatten)] + flat: (), + } + + // The non-flattened struct must be behind a level of flatten + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct Inner { + flat: Flattened, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct FlattenedStruct { + ho: i32, + #[serde(flatten)] + other: HashMap, + #[serde(flatten)] + inner: Inner, + } + + assert_eq!( + check_roundtrip( + &FlattenedStruct { + ho: 24, + other: HashMap::new(), + inner: Inner { + flat: Flattened { hi: 42, flat: () }, + }, + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("i32"), + found: String::from("a map") + }, + position: Position { line: 6, col: 1 } + })) + ); +} + +#[test] +fn flatten_struct_beside_map_inside_flatten_struct_variant() { + // The non-flattened struct must contain some flattened fields + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct Flattened { + hi: i32, + #[serde(flatten)] + flat: (), + } + + // The non-flattened struct must be behind a level of flatten + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct Inner { + flat: Flattened, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum FlattenedStructVariant { + A { + ho: i32, + #[serde(flatten)] + other: HashMap, + #[serde(flatten)] + inner: Inner, + }, + } + + assert_eq!( + check_roundtrip( + &FlattenedStructVariant::A { + ho: 24, + other: HashMap::new(), + inner: Inner { + flat: Flattened { hi: 42, flat: () }, + }, + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("i32"), + found: String::from("a map") + }, + position: Position { line: 6, col: 1 } + })) + ); +} + +#[test] +fn untagged_flatten_struct_variant_beside_map_inside_flatten_struct() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(untagged)] + enum Flattened { + Struct { + hi: i32, + #[serde(flatten)] + flat: (), + }, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct FlattenedStruct { + ho: i32, + #[serde(flatten)] + other: HashMap, + #[serde(flatten)] + flat: Flattened, + } + + assert_eq!( + check_roundtrip( + &FlattenedStruct { + ho: 24, + other: HashMap::new(), + flat: Flattened::Struct { hi: 42, flat: () }, + }, + PrettyConfig::default() + ), + Err(Ok(Error::Message(String::from("ROUNDTRIP error: FlattenedStruct { ho: 24, other: {}, flat: Struct { hi: 42, flat: () } } != FlattenedStruct { ho: 24, other: {\"hi\": 42}, flat: Struct { hi: 42, flat: () } }")))) + ); +} + +#[test] +fn untagged_flatten_struct_variant_beside_map_inside_flatten_struct_variant() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(untagged)] + enum Flattened { + Struct { + hi: i32, + #[serde(flatten)] + flat: (), + }, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum FlattenedStructVariant { + A { + ho: i32, + #[serde(flatten)] + other: HashMap, + #[serde(flatten)] + flat: Flattened, + }, + } + + assert_eq!( + check_roundtrip( + &FlattenedStructVariant::A { + ho: 24, + other: HashMap::new(), + flat: Flattened::Struct { hi: 42, flat: () }, + }, + PrettyConfig::default() + ), + Err(Ok(Error::Message(String::from("ROUNDTRIP error: A { ho: 24, other: {}, flat: Struct { hi: 42, flat: () } } != A { ho: 24, other: {\"hi\": 42}, flat: Struct { hi: 42, flat: () } }")))) + ); +} + +#[test] +fn externally_tagged_newtype_variant_beside_map_inside_flatten_struct_variant() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum Flattened { + Newtype(()), + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum FlattenedStructVariant { + A { + ho: i32, + #[serde(flatten)] + other: HashMap, + #[serde(flatten)] + flat: Flattened, + }, + } + + assert_eq!( + check_roundtrip( + &FlattenedStructVariant::A { + ho: 24, + other: HashMap::new(), + flat: Flattened::Newtype(()), + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("i32"), + found: String::from("a unit value") + }, + position: Position { line: 4, col: 1 } + })) + ); +} + +#[test] +fn externally_tagged_struct_variant_beside_map_inside_flatten_struct() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum Flattened { + Struct { hi: i32 }, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct FlattenedStruct { + ho: i32, + #[serde(flatten)] + other: HashMap, + #[serde(flatten)] + flat: Flattened, + } + + assert_eq!( + check_roundtrip( + &FlattenedStruct { + ho: 24, + other: HashMap::new(), + flat: Flattened::Struct { hi: 42 }, + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("i32"), + found: String::from("a map") + }, + position: Position { line: 6, col: 1 } + })) + ); +} + +#[test] +fn externally_tagged_tuple_variant_beside_map_inside_flatten_struct_variant() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum Flattened { + Tuple(i32, bool), + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum FlattenedStructVariant { + A { + ho: i32, + #[serde(flatten)] + other: HashMap, + #[serde(flatten)] + flat: Flattened, + }, + } + + assert_eq!( + check_roundtrip( + &FlattenedStructVariant::A { + ho: 24, + other: HashMap::new(), + flat: Flattened::Tuple(42, true), + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("i32"), + found: String::from("a sequence") + }, + position: Position { line: 7, col: 1 } + })) + ); +} + +#[test] +fn internally_tagged_unit_variant_beside_map_inside_flatten_struct() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag")] + enum Flattened { + Unit, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct FlattenedStruct { + ho: i32, + #[serde(flatten)] + other: HashMap, + #[serde(flatten)] + flat: Flattened, + } + + assert_eq!( + check_roundtrip( + &FlattenedStruct { + ho: 24, + other: HashMap::new(), + flat: Flattened::Unit, + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("i32"), + found: String::from("the string \"Unit\"") + }, + position: Position { line: 4, col: 1 } + })) + ); +} + +#[test] +fn internally_tagged_newtype_variant_beside_map_inside_flatten_struct_variant() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag")] + enum Flattened { + Newtype(()), + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum FlattenedStructVariant { + A { + ho: i32, + #[serde(flatten)] + other: HashMap, + #[serde(flatten)] + flat: Flattened, + }, + } + + assert_eq!( + check_roundtrip( + &FlattenedStructVariant::A { + ho: 24, + other: HashMap::new(), + flat: Flattened::Newtype(()), + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("i32"), + found: String::from("the string \"Newtype\"") + }, + position: Position { line: 4, col: 1 } + })) + ); +} + +#[test] +fn internally_tagged_struct_variant_beside_map_inside_flatten_struct() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag")] + enum Flattened { + Struct { hi: i32 }, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct FlattenedStruct { + ho: i32, + #[serde(flatten)] + other: HashMap, + #[serde(flatten)] + flat: Flattened, + } + + assert_eq!( + check_roundtrip( + &FlattenedStruct { + ho: 24, + other: HashMap::new(), + flat: Flattened::Struct { hi: 42 }, + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("i32"), + found: String::from("the string \"Struct\"") + }, + position: Position { line: 5, col: 1 } + })) + ); +} + +#[test] +fn adjacently_tagged_unit_variant_beside_map_inside_flatten_struct() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag", content = "content")] + enum Flattened { + Unit, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct FlattenedStruct { + ho: i32, + #[serde(flatten)] + other: HashMap, + #[serde(flatten)] + flat: Flattened, + } + + assert_eq!( + check_roundtrip( + &FlattenedStruct { + ho: 24, + other: HashMap::new(), + flat: Flattened::Unit, + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("i32"), + found: String::from("the string \"Unit\"") + }, + position: Position { line: 4, col: 1 } + })) + ); +} + +#[test] +fn adjacently_tagged_newtype_variant_beside_map_inside_flatten_struct_variant() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag", content = "content")] + enum Flattened { + Newtype(()), + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum FlattenedStructVariant { + A { + ho: i32, + #[serde(flatten)] + other: HashMap, + #[serde(flatten)] + flat: Flattened, + }, + } + + assert_eq!( + check_roundtrip( + &FlattenedStructVariant::A { + ho: 24, + other: HashMap::new(), + flat: Flattened::Newtype(()), + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("i32"), + found: String::from("the string \"Newtype\"") + }, + position: Position { line: 5, col: 1 } + })) + ); +} + +#[test] +fn adjacently_tagged_struct_variant_beside_map_inside_flatten_struct() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag", content = "content")] + enum Flattened { + Struct { hi: i32 }, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct FlattenedStruct { + ho: i32, + #[serde(flatten)] + other: HashMap, + #[serde(flatten)] + flat: Flattened, + } + + assert_eq!( + check_roundtrip( + &FlattenedStruct { + ho: 24, + other: HashMap::new(), + flat: Flattened::Struct { hi: 42 }, + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("i32"), + found: String::from("the string \"Struct\"") + }, + position: Position { line: 7, col: 1 } + })) + ); +} + +#[test] +fn adjacently_tagged_tuple_variant_beside_map_inside_flatten_struct_variant() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag", content = "content")] + enum Flattened { + Tuple(i32, bool), + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum FlattenedStructVariant { + A { + ho: i32, + #[serde(flatten)] + other: HashMap, + #[serde(flatten)] + flat: Flattened, + }, + } + + assert_eq!( + check_roundtrip( + &FlattenedStructVariant::A { + ho: 24, + other: HashMap::new(), + flat: Flattened::Tuple(42, true), + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("i32"), + found: String::from("the string \"Tuple\"") + }, + position: Position { line: 5, col: 1 } + })) + ); +} + +#[test] +fn tagged_struct_beside_map_inside_flatten_struct() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag")] + struct Flattened { + hi: i32, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct FlattenedStruct { + ho: i32, + #[serde(flatten)] + other: HashMap, + #[serde(flatten)] + flat: Flattened, + } + + assert_eq!( + check_roundtrip( + &FlattenedStruct { + ho: 24, + other: HashMap::new(), + flat: Flattened { hi: 42 }, + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("i32"), + found: String::from("the string \"Flattened\"") + }, + position: Position { line: 5, col: 1 } + })) + ); +} + +#[test] +fn tagged_struct_beside_map_inside_flatten_struct_variant() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag")] + struct Flattened { + hi: i32, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum FlattenedStructVariant { + A { + ho: i32, + #[serde(flatten)] + other: HashMap, + #[serde(flatten)] + flat: Flattened, + }, + } + + assert_eq!( + check_roundtrip( + &FlattenedStructVariant::A { + ho: 24, + other: HashMap::new(), + flat: Flattened { hi: 42 }, + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("i32"), + found: String::from("the string \"Flattened\"") + }, + position: Position { line: 5, col: 1 } + })) + ); +} + +#[test] +fn zero_length_untagged_tuple_variant() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(untagged)] + enum Untagged { + A(), + } + + assert_eq!( + check_roundtrip(&Untagged::A(), PrettyConfig::default()), + Err(Err(SpannedError { + code: Error::Message(String::from( + "data did not match any variant of untagged enum Untagged" + )), + position: Position { line: 1, col: 3 } + })) + ); +} + +#[test] +fn zero_length_untagged_struct_variant() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(untagged)] + enum Untagged { + A {}, + } + + assert_eq!( + check_roundtrip(&Untagged::A {}, PrettyConfig::default()), + Err(Err(SpannedError { + code: Error::Message(String::from( + "data did not match any variant of untagged enum Untagged" + )), + position: Position { line: 1, col: 3 } + })) + ); +} + +#[test] +fn unwrapped_one_element_untagged_tuple_variant() { + // A tuple variant with just one element that is not a newtype variant + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(untagged)] + enum Untagged { + OneTuple(i32, #[serde(skip)] ()), + } + + assert_eq!( + check_roundtrip(&Untagged::OneTuple(42, ()), PrettyConfig::default()), + Ok(()) + ); + assert_eq!( + check_roundtrip( + &Untagged::OneTuple(42, ()), + PrettyConfig::default().extensions(Extensions::UNWRAP_VARIANT_NEWTYPES) + ), + Ok(()) + ); +} + +#[test] +fn unit_inside_untagged_newtype_variant_inside_internally_tagged_newtype_variant() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(untagged)] + enum Untagged { + Newtype(()), + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag")] + enum InternallyTagged { + Newtype(Untagged), + } + + assert_eq!( + check_roundtrip( + &InternallyTagged::Newtype(Untagged::Newtype(())), + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::Message(String::from( + "data did not match any variant of untagged enum Untagged" + )), + position: Position { line: 3, col: 2 } + })) + ); +} + +#[test] +fn unit_inside_untagged_newtype_variant_inside_flatten_struct() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(untagged)] + enum Untagged { + Newtype(()), + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct FlattenedStruct { + ho: i32, + #[serde(flatten)] + a: Untagged, + } + + assert_eq!( + check_roundtrip( + &FlattenedStruct { + ho: 24, + a: Untagged::Newtype(()), + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::Message(String::from( + "data did not match any variant of untagged enum Untagged" + )), + position: Position { line: 3, col: 1 } + })) + ); +} + +#[test] +fn unit_struct_inside_untagged_newtype_variant_inside_internally_tagged_newtype_variant() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct Unit; + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(untagged)] + enum Untagged { + Newtype(Unit), + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag")] + enum InternallyTagged { + Newtype(Untagged), + } + + assert_eq!( + check_roundtrip( + &InternallyTagged::Newtype(Untagged::Newtype(Unit)), + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::Message(String::from( + "data did not match any variant of untagged enum Untagged" + )), + position: Position { line: 3, col: 2 } + })) + ); +} + +#[test] +fn untagged_unit_variant_inside_internally_tagged_newtype_variant() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(untagged)] + enum Untagged { + Unit, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum FlattenedStructVariant { + C { + ho: i32, + #[serde(flatten)] + a: Untagged, + }, + } + + assert_eq!( + check_roundtrip( + &FlattenedStructVariant::C { + ho: 24, + a: Untagged::Unit, + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::Message(String::from( + "data did not match any variant of untagged enum Untagged" + )), + position: Position { line: 3, col: 1 } + })) + ); +} + +#[test] +fn untagged_unit_variant_inside_flatten_struct_variant() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum Enum { + Unit, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(untagged)] + enum Untagged { + Unit, + Newtype(Enum), + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag")] + enum InternallyTagged { + Newtype(Untagged), + } + + assert_eq!( + check_roundtrip( + &InternallyTagged::Newtype(Untagged::Newtype(Enum::Unit)), + PrettyConfig::default() + ), + Ok(()) + ); + assert_eq!( + check_roundtrip( + &InternallyTagged::Newtype(Untagged::Unit), + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::Message(String::from( + "data did not match any variant of untagged enum Untagged" + )), + position: Position { line: 3, col: 2 } + })) + ); +} + +#[test] +fn unit_inside_internally_tagged_newtype_variant_inside_multi_flatten_struct() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct AnotherFlattenedStruct { + hi: i32, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag")] + enum InternallyTagged { + Newtype(()), + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct FlattenedStruct { + ho: i32, + #[serde(flatten)] + a: InternallyTagged, + #[serde(flatten)] + b: AnotherFlattenedStruct, + } + + assert_eq!( + check_roundtrip( + &FlattenedStruct { + ho: 24, + a: InternallyTagged::Newtype(()), + b: AnotherFlattenedStruct { hi: 42 }, + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("unit"), + found: String::from("a map") + }, + position: Position { line: 5, col: 1 } + })) + ); +} + +#[test] +fn untagged_unit_variant_inside_internally_tagged_newtype_variant_inside_multi_flatten_struct() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(untagged)] + enum Untagged { + Unit, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct AnotherFlattenedStruct { + hi: i32, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag")] + enum InternallyTagged { + Newtype(Untagged), + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct FlattenedStruct { + ho: i32, + #[serde(flatten)] + a: InternallyTagged, + #[serde(flatten)] + b: AnotherFlattenedStruct, + } + + assert_eq!( + check_roundtrip( + &FlattenedStruct { + ho: 24, + a: InternallyTagged::Newtype(Untagged::Unit), + b: AnotherFlattenedStruct { hi: 42 }, + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::Message(String::from( + "data did not match any variant of untagged enum Untagged" + )), + position: Position { line: 5, col: 1 } + })) + ); +} + +#[test] +fn flattened_externally_tagged_newtype_variant_beside_flattened_intenally_tagged_enum() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum ExternallyTagged { + Newtype(()), + Other(()), + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag")] + enum InternallyTagged { + Newtype(ExternallyTagged), + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct Flattened { + #[serde(flatten)] + a: InternallyTagged, + #[serde(flatten)] + b: ExternallyTagged, + } + + assert_eq!( + check_roundtrip( + &Flattened { + a: InternallyTagged::Newtype(ExternallyTagged::Other(())), + b: ExternallyTagged::Newtype(()), + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("map with a single key"), + found: String::from("a map"), + }, + position: Position { line: 5, col: 1 } + })) + ); +} + +#[test] +fn flattened_externally_tagged_struct_variant_beside_flattened_intenally_tagged_enum() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + enum ExternallyTagged { + Struct { a: i32 }, + Other(()), + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(tag = "tag")] + enum InternallyTagged { + Newtype(ExternallyTagged), + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct Flattened { + #[serde(flatten)] + a: InternallyTagged, + #[serde(flatten)] + b: ExternallyTagged, + } + + assert_eq!( + check_roundtrip( + &Flattened { + a: InternallyTagged::Newtype(ExternallyTagged::Other(())), + b: ExternallyTagged::Struct { a: 42 }, + }, + PrettyConfig::default() + ), + Err(Err(SpannedError { + code: Error::InvalidValueForType { + expected: String::from("map with a single key"), + found: String::from("a map"), + }, + position: Position { line: 7, col: 1 } + })) + ); +} + +#[test] +fn flattened_map_inside_option_beside_flattened_struct_variant() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(untagged)] + enum Untagged { + Struct { a: i32 }, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct Flattened { + #[serde(flatten)] + a: Untagged, + #[serde(flatten)] + b: Option>, + } + + assert_eq!( + check_roundtrip( + &Flattened { + a: Untagged::Struct { + a: 42, + }, + b: Some([(String::from("b"), 24)].into_iter().collect()), + }, + PrettyConfig::default() + ), + Err(Ok(Error::Message(String::from("ROUNDTRIP error: Flattened { a: Struct { a: 42 }, b: Some({\"b\": 24}) } != Flattened { a: Struct { a: 42 }, b: Some({\"a\": 42, \"b\": 24}) }")))) + ); +} + +#[test] +fn flattened_untagged_struct_beside_flattened_untagged_struct() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + #[serde(untagged)] + enum Untagged { + Struct { a: i32 }, + Other { b: i32 }, + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct Flattened { + #[serde(flatten)] + a: Untagged, + #[serde(flatten)] + b: Untagged, + } + + assert_eq!( + check_roundtrip( + &Flattened { + a: Untagged::Struct { + a: 42, + }, + b: Untagged::Other { + b: 24, + }, + }, + PrettyConfig::default() + ), + Err(Ok(Error::Message(String::from("ROUNDTRIP error: Flattened { a: Struct { a: 42 }, b: Other { b: 24 } } != Flattened { a: Struct { a: 42 }, b: Struct { a: 42 } }")))) + ); +} + +#[test] +fn flattened_field_inside_flattened_struct_alongside_map() { + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct Units { + a: i32, + #[serde(flatten)] + b: (), + } + + #[derive(PartialEq, Debug, Serialize, Deserialize)] + struct Flattened { + #[serde(flatten)] + a: Units, + #[serde(flatten)] + b: BTreeMap, + } + + assert_eq!( + check_roundtrip( + &Flattened { + a: Units { + a: 42, + b: (), + }, + b: [(String::from("c"), 24)].into_iter().collect(), + }, + PrettyConfig::default() + ), + Err(Ok(Error::Message(String::from("ROUNDTRIP error: Flattened { a: Units { a: 42, b: () }, b: {\"c\": 24} } != Flattened { a: Units { a: 42, b: () }, b: {\"a\": 42, \"c\": 24} }")))) + ); +} + +fn check_roundtrip( + val: &T, + config: PrettyConfig, +) -> Result<(), Result> { + let ron = ron::ser::to_string_pretty(val, config).map_err(|err| Ok(err))?; + println!("{ron}"); + let de = ron::from_str(&ron).map_err(|err| Err(err))?; + if val == &de { + Ok(()) + } else { + Err(Ok(Error::Message(format!( + "ROUNDTRIP error: {:?} != {:?}", + val, de + )))) + } +} diff --git a/third_party/rust/ron/tests/508_value_adjacently_tagged_bug.rs b/third_party/rust/ron/tests/508_value_adjacently_tagged_bug.rs new file mode 100644 index 000000000000..c237c2ca0f4c --- /dev/null +++ b/third_party/rust/ron/tests/508_value_adjacently_tagged_bug.rs @@ -0,0 +1,59 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +#[serde(tag = "type", content = "value")] +enum TheEnum { + Variant([f32; 3]), +} + +#[test] +fn roundtrip_through_value() { + let value = TheEnum::Variant([0.1, 0.1, 0.1]); + + let ron = ron::to_string(&value).unwrap(); + assert_eq!(ron, "(type:Variant,value:(0.1,0.1,0.1))"); + + let de = ron::from_str::(&ron).unwrap(); + assert_eq!(de, value); + + let ron_value = ron::from_str::(&ron).unwrap(); + + // Known bug: ron::Value only stores a unit, cannot find a variant + let err = ron_value.into_rust::().unwrap_err(); + assert_eq!( + err, + ron::Error::InvalidValueForType { + expected: String::from("variant of enum TheEnum"), + found: String::from("a unit value") + } + ); + + let old_serde_ron: &str = "(type:\"Variant\",value:(0.1,0.1,0.1))"; + + // Known bug: serde no longer uses strings in > v1.0.180 to deserialize the variant + let err = ron::from_str::(&old_serde_ron).unwrap_err(); + assert_eq!( + err, + ron::error::SpannedError { + code: ron::Error::ExpectedIdentifier, + position: ron::error::Position { line: 1, col: 7 }, + } + ); + + let ron_value = ron::from_str::(&old_serde_ron).unwrap(); + + // Known bug: ron::Value is asked for an enum but has no special handling for it (yet) + let err = ron_value.into_rust::().unwrap_err(); + assert_eq!( + err, + ron::Error::InvalidValueForType { + expected: String::from("variant of enum TheEnum"), + found: String::from("the string \"Variant\"") + } + ); + + // This still works, but is a bug as well + let ron_value = ron::from_str::("(\"Variant\",(0.1,0.1,0.1))").unwrap(); + let de: TheEnum = ron_value.into_rust::().unwrap(); + assert_eq!(de, value); +} diff --git a/third_party/rust/ron/tests/511_deserialize_any_map_string_key.rs b/third_party/rust/ron/tests/511_deserialize_any_map_string_key.rs new file mode 100644 index 000000000000..1ebed521e1ac --- /dev/null +++ b/third_party/rust/ron/tests/511_deserialize_any_map_string_key.rs @@ -0,0 +1,76 @@ +#[test] +fn test_map_custom_deserialize() { + use std::collections::HashMap; + + #[derive(PartialEq, Debug)] + struct CustomMap(HashMap); + + // Use a custom deserializer for CustomMap in order to extract String + // keys in the visit_map method. + impl<'de> serde::de::Deserialize<'de> for CustomMap { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct CVisitor; + impl<'de> serde::de::Visitor<'de> for CVisitor { + type Value = CustomMap; + + // GRCOV_EXCL_START + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(formatter, "a map with string keys and values") + } + // GRCOV_EXCL_STOP + + fn visit_map(self, mut map: A) -> Result + where + A: serde::de::MapAccess<'de>, + { + let mut inner = HashMap::new(); + while let Some((k, v)) = map.next_entry::()? { + inner.insert(k, v); + } + Ok(CustomMap(inner)) + } + } + // Note: This method will try to deserialize any value. In this test, it will + // invoke the visit_map method in the visitor. + deserializer.deserialize_any(CVisitor) + } + } + + let mut map = HashMap::::new(); + map.insert("key1".into(), "value1".into()); + map.insert("key2".into(), "value2".into()); + + let result: Result = ron::from_str( + r#"( + key1: "value1", + key2: "value2", + )"#, + ); + + assert_eq!(result, Ok(CustomMap(map))); +} + +#[test] +fn test_ron_struct_as_json_map() { + let json: serde_json::Value = ron::from_str("(f1: 0, f2: 1)").unwrap(); + assert_eq!( + json, + serde_json::Value::Object( + [ + ( + String::from("f1"), + serde_json::Value::Number(serde_json::Number::from(0)) + ), + ( + String::from("f2"), + serde_json::Value::Number(serde_json::Number::from(1)) + ), + ] + .into_iter() + .collect() + ) + ); +} diff --git a/third_party/rust/ron/tests/522_explicit_struct_names.rs b/third_party/rust/ron/tests/522_explicit_struct_names.rs new file mode 100644 index 000000000000..7a9a7d463235 --- /dev/null +++ b/third_party/rust/ron/tests/522_explicit_struct_names.rs @@ -0,0 +1,109 @@ +use ron::{ + extensions::Extensions, + from_str, + ser::{to_string_pretty, PrettyConfig}, + Error, Options, +}; +use serde_derive::{Deserialize, Serialize}; + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +struct Id(u32); + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +struct Position(f32, f32); + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +enum Query { + None, + Creature(Id), + Location(Position), +} + +#[derive(Debug, PartialEq, Serialize, Deserialize)] +struct Foo { + #[allow(unused)] + pub id: Id, + #[allow(unused)] + pub position: Position, + #[allow(unused)] + pub query: Query, +} + +const EXPECT_ERROR_MESSAGE: &'static str = + "expected `Err(Error::ExpectedStructName)`, deserializer returned `Ok`"; + +#[test] +fn explicit_struct_names() { + let options = Options::default().with_default_extension(Extensions::EXPLICIT_STRUCT_NAMES); + let foo_ser = Foo { + id: Id(3), + position: Position(0.0, 8.72), + query: Query::Creature(Id(4)), + }; + + // phase 1 (regular structs) + let content_regular = r#"( + id: Id(3), + position: Position(0.0, 8.72), + query: None, + )"#; + let foo = options.from_str::(content_regular); + assert_eq!( + foo.expect_err(EXPECT_ERROR_MESSAGE).code, + Error::ExpectedStructName("Foo".to_string()) + ); + + // phase 2 (newtype structs) + let content_newtype = r#"Foo( + id: (3), + position: Position(0.0, 8.72), + query: None, + )"#; + let foo = options.from_str::(content_newtype); + assert_eq!( + foo.expect_err(EXPECT_ERROR_MESSAGE).code, + Error::ExpectedStructName("Id".to_string()) + ); + + // phase 3 (tuple structs) + let content_tuple = r#"Foo( + id: Id(3), + position: (0.0, 8.72), + query: None, + )"#; + let foo = options.from_str::(content_tuple); + assert_eq!( + foo.expect_err(EXPECT_ERROR_MESSAGE).code, + Error::ExpectedStructName("Position".to_string()) + ); + + // phase 4 (test without this extension) + let _foo1 = from_str::(content_regular).unwrap(); + let _foo2 = from_str::(content_newtype).unwrap(); + let _foo3 = from_str::(content_tuple).unwrap(); + + // phase 5 (test serialization) + let pretty_config = PrettyConfig::new() + .extensions(Extensions::EXPLICIT_STRUCT_NAMES | Extensions::UNWRAP_VARIANT_NEWTYPES); + let content = to_string_pretty(&foo_ser, pretty_config).unwrap(); + assert_eq!( + content, + r#"#![enable(unwrap_variant_newtypes)] +#![enable(explicit_struct_names)] +Foo( + id: Id(3), + position: Position(0.0, 8.72), + query: Creature(4), +)"# + ); + let foo_de = from_str::(&content); + match foo_de { + // GRCOV_EXCL_START + Err(err) => panic!( + "failed to deserialize with `explicit_struct_names` and `unwrap_variant_newtypes`: {}", + err + ), + // GRCOV_EXCL_STOP + Ok(foo_de) => assert_eq!(foo_de, foo_ser), + } +} diff --git a/third_party/rust/ron/tests/526_flatten.rs b/third_party/rust/ron/tests/526_flatten.rs new file mode 100644 index 000000000000..7bc56168b85f --- /dev/null +++ b/third_party/rust/ron/tests/526_flatten.rs @@ -0,0 +1,54 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +struct SomeCollection { + inner: Vec, +} + +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +struct SomeItem { + #[serde(flatten)] + foo: Foo, + #[serde(flatten)] + bar: Bar, +} + +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +struct Bar { + name: String, + some_enum: Option, +} + +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +struct Foo { + something: String, +} + +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] +enum SomeEnum { + A, + B, +} + +#[test] +fn roundtrip() { + let scene = SomeCollection { + inner: vec![SomeItem { + foo: Foo { + something: "something".to_string(), + }, + bar: Bar { + name: "name".to_string(), + some_enum: Some(SomeEnum::A), + }, + }], + }; + + let ron = ron::ser::to_string(&scene).unwrap(); + let de: SomeCollection = ron::de::from_str(&ron).unwrap(); + assert_eq!(de, scene); + + let ron = ron::ser::to_string_pretty(&scene, Default::default()).unwrap(); + let _deser_scene: SomeCollection = ron::de::from_str(&ron).unwrap(); + assert_eq!(de, scene); +} diff --git a/third_party/rust/ron/tests/530_untagged_union.rs b/third_party/rust/ron/tests/530_untagged_union.rs new file mode 100644 index 000000000000..37e8ffe5094f --- /dev/null +++ b/third_party/rust/ron/tests/530_untagged_union.rs @@ -0,0 +1,21 @@ +use std::ops::Bound; + +#[derive(serde::Deserialize, serde::Serialize, PartialEq, Eq, Debug)] +#[serde(untagged)] +enum EitherInterval { + B(Interval), + D(V, Option), +} + +type Interval = (Bound, Bound); + +#[test] +fn serde_roundtrip() { + let interval = EitherInterval::B((Bound::Excluded(0u8), Bound::Unbounded)); + + let ron: String = ron::ser::to_string(&interval).unwrap(); + assert_eq!(ron, "(Excluded(0),Unbounded)"); + + let de = ron::de::from_str(&ron).unwrap(); + assert_eq!(interval, de); +} diff --git a/third_party/rust/ron/tests/544_path_meta.rs b/third_party/rust/ron/tests/544_path_meta.rs new file mode 100644 index 000000000000..a165bcf73a84 --- /dev/null +++ b/third_party/rust/ron/tests/544_path_meta.rs @@ -0,0 +1,129 @@ +use ron::ser::{path_meta::Field, PrettyConfig}; + +#[test] +fn serialize_field() { + #[derive(serde::Serialize)] + enum PetKind { + Isopod, + } + + #[derive(serde::Serialize)] + struct Pet { + name: &'static str, + age: u8, + kind: PetKind, + } + + #[derive(serde::Serialize)] + struct Person { + name: &'static str, + age: u8, + knows: Vec, + pet: Option, + } + + let value = ( + Person { + name: "Walter", + age: 43, + knows: vec![0, 1], + pet: None, + }, + vec![ + Person { + name: "Alice", + age: 29, + knows: vec![1], + pet: Some(Pet { + name: "Herbert", + age: 7, + kind: PetKind::Isopod, + }), + }, + Person { + name: "Bob", + age: 29, + knows: vec![0], + pet: None, + }, + ], + ); + + let mut config = PrettyConfig::default(); + + config + .path_meta + .get_or_insert_with(Field::empty) + .build_fields(|fields| { + fields + .field("age") + .with_doc("0@age (person)\nmust be within range 0..256"); + fields + .field("knows") + .with_doc("0@knows (person)\nmust be list of valid person indices"); + fields.field("pet").build_fields(|fields| { + fields + .field("age") + .with_doc("1@age (pet)\nmust be valid range 0..256"); + fields + .field("kind") + .with_doc("1@kind (pet)\nmust be `Isopod`"); + }); + + // provide meta for a field that doesn't exist; + // this should not end up anywhere in the final string + fields.field("0").with_doc("unreachable"); + }); + + let s = ron::ser::to_string_pretty(&value, config).unwrap(); + + assert_eq!( + s, + r#"(( + name: "Walter", + /// 0@age (person) + /// must be within range 0..256 + age: 43, + /// 0@knows (person) + /// must be list of valid person indices + knows: [ + 0, + 1, + ], + pet: None, +), [ + ( + name: "Alice", + /// 0@age (person) + /// must be within range 0..256 + age: 29, + /// 0@knows (person) + /// must be list of valid person indices + knows: [ + 1, + ], + pet: Some(( + name: "Herbert", + /// 1@age (pet) + /// must be valid range 0..256 + age: 7, + /// 1@kind (pet) + /// must be `Isopod` + kind: Isopod, + )), + ), + ( + name: "Bob", + /// 0@age (person) + /// must be within range 0..256 + age: 29, + /// 0@knows (person) + /// must be list of valid person indices + knows: [ + 0, + ], + pet: None, + ), +])"# + ); +} diff --git a/third_party/rust/ron/tests/comments.rs b/third_party/rust/ron/tests/comments.rs index 2b77de618755..8d605400a14c 100644 --- a/third_party/rust/ron/tests/comments.rs +++ b/third_party/rust/ron/tests/comments.rs @@ -32,6 +32,13 @@ fn test_nested() { #[test] fn test_unclosed() { + assert_eq!( + from_str::("\"hi\" /*"), + Err(RonErr { + code: Error::UnclosedBlockComment, + position: Position { line: 1, col: 8 } + }) + ); assert_eq!( from_str::( "/* @@ -46,7 +53,18 @@ fn test_unclosed() { ), Err(RonErr { code: Error::UnclosedBlockComment, - position: Position { col: 1, line: 9 } + position: Position { line: 9, col: 1 } + }) + ); +} + +#[test] +fn test_unexpected_byte() { + assert_eq!( + from_str::("42 /q"), + Err(RonErr { + code: Error::UnexpectedChar('q'), + position: Position { line: 1, col: 6 }, }) ); } diff --git a/third_party/rust/ron/tests/depth_limit.rs b/third_party/rust/ron/tests/depth_limit.rs index ee61a09c89cb..e64cacd145b0 100644 --- a/third_party/rust/ron/tests/depth_limit.rs +++ b/third_party/rust/ron/tests/depth_limit.rs @@ -53,7 +53,7 @@ fn depth_limit() { .depth_limit(1) .separate_tuple_members(true) .enumerate_arrays(true) - .new_line("\n".to_string()); + .new_line("\n"); let s = ron::ser::to_string_pretty(&data, pretty); assert_eq!(s, Ok(EXPECTED.to_string())); diff --git a/third_party/rust/ron/tests/escape.rs b/third_party/rust/ron/tests/escape.rs index 2874c3f4faa3..eec698609e35 100644 --- a/third_party/rust/ron/tests/escape.rs +++ b/third_party/rust/ron/tests/escape.rs @@ -12,6 +12,22 @@ fn test_escape_basic() { assert_eq!(from_str::("\'\\x07\'").unwrap(), '\x07'); assert_eq!(from_str::("\'\\u{7}\'").unwrap(), '\x07'); + + assert_eq!( + from_str::("\'\\u{}\'").unwrap_err(), + ron::error::SpannedError { + code: ron::Error::InvalidEscape("Expected 1-6 digits, got 0 digits in Unicode escape"), + position: ron::error::Position { line: 1, col: 5 }, + } + ); + + assert_eq!( + from_str::("\'\\q\'").unwrap_err(), + ron::error::SpannedError { + code: ron::Error::InvalidEscape("Unknown escape character"), + position: ron::error::Position { line: 1, col: 4 }, + } + ) } fn check_same(t: T) @@ -32,12 +48,12 @@ fn test_ascii_10() { #[test] fn test_ascii_chars() { - (1..128).into_iter().flat_map(from_u32).for_each(check_same) + (1..128).flat_map(from_u32).for_each(check_same) } #[test] fn test_ascii_string() { - let s: String = (1..128).into_iter().flat_map(from_u32).collect(); + let s: String = (1..128).flat_map(from_u32).collect(); check_same(s); } diff --git a/third_party/rust/ron/tests/large_number.rs b/third_party/rust/ron/tests/large_number.rs index c8ae49f0b4fa..8a7bc39d6b0f 100644 --- a/third_party/rust/ron/tests/large_number.rs +++ b/third_party/rust/ron/tests/large_number.rs @@ -14,15 +14,16 @@ fn test_large_number() { #[test] fn test_large_integer_to_float() { - use ron::value::Float; - let test_var = std::i64::MAX as u64 + 1; - let expected = test_var as f64; // Is exactly representable by f64 - let test_ser = ron::ser::to_string(&test_var).unwrap(); - assert_eq!(test_ser, test_var.to_string()); + use ron::value::Number; + let test_var = std::u64::MAX as u128 + 1; + let test_ser = test_var.to_string(); let test_deser = ron::de::from_str::(&test_ser); + #[cfg(not(feature = "integer128"))] assert_eq!( test_deser.unwrap(), - Value::Number(Number::Float(Float::new(expected))), + Value::Number(Number::F32((test_var as f32).into())), // f64 representation matches f32 ); + #[cfg(feature = "integer128")] + assert_eq!(test_deser.unwrap(), Value::Number(Number::U128(test_var)),); } diff --git a/third_party/rust/ron/tests/non_identifier_identifier.rs b/third_party/rust/ron/tests/non_identifier_identifier.rs index 81b6c2cdc6b1..9cb75feb7497 100644 --- a/third_party/rust/ron/tests/non_identifier_identifier.rs +++ b/third_party/rust/ron/tests/non_identifier_identifier.rs @@ -79,7 +79,11 @@ test_non_identifier! { test_u128 => deserialize_u128() } test_non_identifier! { test_f32 => deserialize_f32() } test_non_identifier! { test_f64 => deserialize_f64() } test_non_identifier! { test_char => deserialize_char() } -test_non_identifier! { test_string => deserialize_string() } +// Removed due to fix for #511 - string keys are allowed. +// test_non_identifier! { test_string => deserialize_string() } +// See comment above. If deserialize_str is to be added, it should give the same expected result as +// deserialize_string. deserialize_str and deserialize_string should be consistently implemented. +// test_non_identifier! { test_str => deserialize_str() } test_non_identifier! { test_bytes => deserialize_bytes() } test_non_identifier! { test_byte_buf => deserialize_byte_buf() } test_non_identifier! { test_option => deserialize_option() } diff --git a/third_party/rust/ron/tests/non_string_tag.rs b/third_party/rust/ron/tests/non_string_tag.rs new file mode 100644 index 000000000000..6c6c206e595c --- /dev/null +++ b/third_party/rust/ron/tests/non_string_tag.rs @@ -0,0 +1,143 @@ +macro_rules! test_non_tag { + ($test_name:ident => $deserialize_method:ident($($deserialize_param:expr),*)) => { + #[test] + fn $test_name() { + use serde::{Deserialize, Deserializer, de::Visitor}; + + struct TagVisitor; + + impl<'de> Visitor<'de> for TagVisitor { + type Value = Tag; + + // GRCOV_EXCL_START + fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.write_str("an error") + } + // GRCOV_EXCL_STOP + } + + struct Tag; + + impl<'de> Deserialize<'de> for Tag { + fn deserialize>(deserializer: D) + -> Result + { + deserializer.$deserialize_method($($deserialize_param,)* TagVisitor) + } + } + + #[derive(Debug)] // GRCOV_EXCL_LINE + struct InternallyTagged; + + impl<'de> Deserialize<'de> for InternallyTagged { + fn deserialize>(deserializer: D) + -> Result + { + deserializer.deserialize_any(serde::__private::de::TaggedContentVisitor::< + Tag, + >::new( + "tag", + "an internally tagged value", + )).map(|_| unreachable!()) + } + } + + assert_eq!( + ron::from_str::("(tag: err)").unwrap_err().code, + ron::Error::ExpectedString + ) + } + }; +} + +test_non_tag! { test_bool => deserialize_bool() } +test_non_tag! { test_i8 => deserialize_i8() } +test_non_tag! { test_i16 => deserialize_i16() } +test_non_tag! { test_i32 => deserialize_i32() } +test_non_tag! { test_i64 => deserialize_i64() } +#[cfg(feature = "integer128")] +test_non_tag! { test_i128 => deserialize_i128() } +test_non_tag! { test_u8 => deserialize_u8() } +test_non_tag! { test_u16 => deserialize_u16() } +test_non_tag! { test_u32 => deserialize_u32() } +test_non_tag! { test_u64 => deserialize_u64() } +#[cfg(feature = "integer128")] +test_non_tag! { test_u128 => deserialize_u128() } +test_non_tag! { test_f32 => deserialize_f32() } +test_non_tag! { test_f64 => deserialize_f64() } +test_non_tag! { test_char => deserialize_char() } +test_non_tag! { test_bytes => deserialize_bytes() } +test_non_tag! { test_byte_buf => deserialize_byte_buf() } +test_non_tag! { test_option => deserialize_option() } +test_non_tag! { test_unit => deserialize_unit() } +test_non_tag! { test_unit_struct => deserialize_unit_struct("") } +test_non_tag! { test_newtype_struct => deserialize_newtype_struct("") } +test_non_tag! { test_seq => deserialize_seq() } +test_non_tag! { test_tuple => deserialize_tuple(0) } +test_non_tag! { test_tuple_struct => deserialize_tuple_struct("", 0) } +test_non_tag! { test_map => deserialize_map() } +test_non_tag! { test_struct => deserialize_struct("", &[]) } +test_non_tag! { test_enum => deserialize_enum("", &[]) } + +macro_rules! test_tag { + ($test_name:ident => $deserialize_method:ident($($deserialize_param:expr),*)) => { + #[test] + fn $test_name() { + use serde::{Deserialize, Deserializer, de::Visitor}; + + struct TagVisitor; + + impl<'de> Visitor<'de> for TagVisitor { + type Value = Tag; + + // GRCOV_EXCL_START + fn expecting(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.write_str("a tag") + } + // GRCOV_EXCL_STOP + + fn visit_str(self, v: &str) -> Result { + assert_eq!(v, "tag"); + Ok(Tag) + } + } + + struct Tag; + + impl<'de> Deserialize<'de> for Tag { + fn deserialize>(deserializer: D) + -> Result + { + deserializer.$deserialize_method($($deserialize_param,)* TagVisitor) + } + } + + #[derive(Debug)] // GRCOV_EXCL_LINE + struct InternallyTagged; + + impl<'de> Deserialize<'de> for InternallyTagged { + fn deserialize>(deserializer: D) + -> Result + { + deserializer.deserialize_any(serde::__private::de::TaggedContentVisitor::< + Tag, + >::new( + "tag", + "an internally tagged value", + )).map(|_| InternallyTagged) + } + } + + assert!( + ron::from_str::("(tag: \"tag\")").is_ok(), + ) + } + }; +} + +test_tag! { test_str => deserialize_string() } +test_tag! { test_string => deserialize_string() } +test_tag! { test_identifier => deserialize_identifier() } + +test_tag! { test_any => deserialize_any() } +test_tag! { test_ignored_any => deserialize_ignored_any() } diff --git a/third_party/rust/ron/tests/options.rs b/third_party/rust/ron/tests/options.rs index 0f609246885e..fbe1bb6f953f 100644 --- a/third_party/rust/ron/tests/options.rs +++ b/third_party/rust/ron/tests/options.rs @@ -14,7 +14,20 @@ fn default_options() { let de: Struct = ron.from_str("(Some(42),(4.2))").unwrap(); let ser = ron.to_string(&de).unwrap(); - assert_eq!(ser, "(Some(42),(4.2))") + assert_eq!(ser, "(Some(42),(4.2))"); +} + +#[test] +fn without_any_options() { + let mut ron = Options::default().with_default_extension(Extensions::all()); + for extension in Extensions::all().iter() { + ron = ron.without_default_extension(extension); + } + + let de: Struct = ron.from_str("(Some(42),(4.2))").unwrap(); + let ser = ron.to_string(&de).unwrap(); + + assert_eq!(ser, "(Some(42),(4.2))"); } #[test] @@ -50,3 +63,46 @@ fn single_default_extension() { assert_eq!(ser, "#![enable(unwrap_newtypes)]\n(42, 4.2)"); } + +#[test] +fn reader_io_error() { + struct Reader<'a> { + buf: &'a [u8], + } + + impl<'a> std::io::Read for Reader<'a> { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + let written = self.buf.read(buf)?; + if written == 0 { + Err(std::io::Error::new(std::io::ErrorKind::BrokenPipe, "oh no")) + } else { + Ok(written) + } + } + } + + assert_eq!( + ron::de::from_reader::(Reader { buf: b"" }).unwrap_err(), + ron::error::SpannedError { + code: ron::Error::Io(String::from("oh no")), + position: ron::error::Position { line: 1, col: 1 }, + } + ); + assert_eq!( + ron::de::from_reader::(Reader { buf: b"hello" }).unwrap_err(), + ron::error::SpannedError { + code: ron::Error::Io(String::from("oh no")), + position: ron::error::Position { line: 1, col: 6 }, + } + ); + assert_eq!( + ron::de::from_reader::(Reader { + buf: b"hello\nmy \xff" + }) + .unwrap_err(), + ron::error::SpannedError { + code: ron::Error::Io(String::from("oh no")), + position: ron::error::Position { line: 2, col: 4 }, + } + ); +} diff --git a/third_party/rust/ron/tests/preserve_sequence.rs b/third_party/rust/ron/tests/preserve_sequence.rs index bdfe3575e9f7..b4155d1bf2f6 100644 --- a/third_party/rust/ron/tests/preserve_sequence.rs +++ b/third_party/rust/ron/tests/preserve_sequence.rs @@ -31,7 +31,7 @@ fn make_roundtrip(source: &str) -> String { .depth_limit(3) .separate_tuple_members(true) .enumerate_arrays(true) - .new_line("\n".into()); + .new_line("\n"); to_string_pretty(&config, pretty).expect("Serialization failed") } diff --git a/third_party/rust/ron/tests/struct_integers.rs b/third_party/rust/ron/tests/struct_integers.rs index 385d53dab35b..e3f4f88b4902 100644 --- a/third_party/rust/ron/tests/struct_integers.rs +++ b/third_party/rust/ron/tests/struct_integers.rs @@ -33,7 +33,6 @@ fn roundtrip() { j: std::u128::MAX, }; let serialized = ron::ser::to_string(&s).unwrap(); - dbg!(&serialized); let deserialized = ron::de::from_str(&serialized).unwrap(); assert_eq!(s, deserialized,); } diff --git a/third_party/rust/ron/tests/unicode.rs b/third_party/rust/ron/tests/unicode.rs index 0617eeed14e9..7651b07bcdb8 100644 --- a/third_party/rust/ron/tests/unicode.rs +++ b/third_party/rust/ron/tests/unicode.rs @@ -1,4 +1,8 @@ -use ron::de::from_str; +use ron::{ + de::{from_bytes, from_str}, + error::Position, + Error, Value, +}; #[test] fn test_char() { @@ -11,3 +15,48 @@ fn test_string() { let de: String = from_str("\"My string: ऄ\"").unwrap(); assert_eq!(de, "My string: ऄ"); } + +#[test] +fn test_char_not_a_comment() { + let _ = from_str::("A('/')").unwrap(); +} + +#[test] +fn ident_starts_with_non_ascii_byte() { + let _ = from_str::("שּׁȬSSSSSSSSSSR").unwrap(); +} + +#[test] +fn test_file_invalid_unicode() { + let error = from_bytes::(&[b'\n', b'a', 0b11000000, 0]).unwrap_err(); + assert!(matches!(error.code, Error::Utf8Error(_))); + assert_eq!(error.position, Position { line: 2, col: 2 }); + let error = from_bytes::(&[b'\n', b'\n', 0b11000000]).unwrap_err(); + assert!(matches!(error.code, Error::Utf8Error(_))); + assert_eq!(error.position, Position { line: 3, col: 1 }); +} + +#[test] +fn serialize_invalid_whitespace() { + assert_eq!( + ron::ser::to_string_pretty(&42, ron::ser::PrettyConfig::default().new_line("a")) + .unwrap_err(), + Error::Message(String::from( + "Invalid non-whitespace `PrettyConfig::new_line`" + )) + ); + assert_eq!( + ron::ser::to_string_pretty(&42, ron::ser::PrettyConfig::default().indentor("a")) + .unwrap_err(), + Error::Message(String::from( + "Invalid non-whitespace `PrettyConfig::indentor`" + )) + ); + assert_eq!( + ron::ser::to_string_pretty(&42, ron::ser::PrettyConfig::default().separator("a")) + .unwrap_err(), + Error::Message(String::from( + "Invalid non-whitespace `PrettyConfig::separator`" + )) + ); +} diff --git a/third_party/rust/ron/tests/value.rs b/third_party/rust/ron/tests/value.rs index 3919058c041c..9b57a4d93904 100644 --- a/third_party/rust/ron/tests/value.rs +++ b/third_party/rust/ron/tests/value.rs @@ -4,46 +4,75 @@ use ron::{ error::Error, value::{Map, Number, Value}, }; -use serde::Serialize; +use serde_derive::{Deserialize, Serialize}; #[test] fn bool() { assert_eq!("true".parse(), Ok(Value::Bool(true))); assert_eq!("false".parse(), Ok(Value::Bool(false))); + + assert_eq!(ron::to_string(&Value::Bool(true)).unwrap(), "true"); + assert_eq!(ron::to_string(&Value::Bool(false)).unwrap(), "false"); } #[test] fn char() { assert_eq!("'a'".parse(), Ok(Value::Char('a'))); + + assert_eq!(ron::to_string(&Value::Char('a')).unwrap(), "'a'"); } #[test] fn map() { let mut map = Map::new(); - map.insert(Value::Char('a'), Value::Number(Number::new(1))); - map.insert(Value::Char('b'), Value::Number(Number::new(2f64))); - assert_eq!("{ 'a': 1, 'b': 2.0 }".parse(), Ok(Value::Map(map))); + map.insert(Value::Char('a'), Value::Number(Number::U8(1))); + map.insert(Value::Char('b'), Value::Number(Number::new(2f32))); + let map = Value::Map(map); + + assert_eq!(ron::to_string(&map).unwrap(), "{'a':1,'b':2.0}"); + + assert_eq!("{ 'a': 1, 'b': 2.0 }".parse(), Ok(map)); } #[test] fn number() { - assert_eq!("42".parse(), Ok(Value::Number(Number::new(42)))); + assert_eq!("42".parse(), Ok(Value::Number(Number::U8(42)))); assert_eq!( "3.141592653589793".parse(), Ok(Value::Number(Number::new(f64::consts::PI))) ); + + assert_eq!( + ron::to_string(&Value::Number(Number::U8(42))).unwrap(), + "42" + ); + assert_eq!( + ron::to_string(&Value::Number(Number::F64(f64::consts::PI.into()))).unwrap(), + "3.141592653589793" + ); } #[test] fn option() { let opt = Some(Box::new(Value::Char('c'))); assert_eq!("Some('c')".parse(), Ok(Value::Option(opt))); + assert_eq!("None".parse(), Ok(Value::Option(None))); + + assert_eq!( + ron::to_string(&Value::Option(Some(Box::new(Value::Char('c'))))).unwrap(), + "Some('c')" + ); + assert_eq!(ron::to_string(&Value::Option(None)).unwrap(), "None"); } #[test] fn string() { let normal = "\"String\""; assert_eq!(normal.parse(), Ok(Value::String("String".into()))); + assert_eq!( + ron::to_string(&Value::String("String".into())).unwrap(), + "\"String\"" + ); let raw = "r\"Raw String\""; assert_eq!(raw.parse(), Ok(Value::String("Raw String".into()))); @@ -62,15 +91,32 @@ fn string() { raw_multi_line.parse(), Ok(Value::String("Multi\nLine".into())) ); + assert_eq!( + ron::to_string(&Value::String("Multi\nLine".into())).unwrap(), + "\"Multi\\nLine\"" + ); +} + +#[test] +fn byte_string() { + assert_eq!( + "b\"\\x01\\u{2}\\0\\x04\"".parse(), + Ok(Value::Bytes(vec![1, 2, 0, 4])) + ); + assert_eq!( + ron::to_string(&Value::Bytes(vec![1, 2, 0, 4])).unwrap(), + "b\"\\x01\\x02\\x00\\x04\"" + ); } #[test] fn seq() { - let seq = vec![ - Value::Number(Number::new(1)), - Value::Number(Number::new(2f64)), - ]; - assert_eq!("[1, 2.0]".parse(), Ok(Value::Seq(seq))); + let seq = Value::Seq(vec![ + Value::Number(Number::U8(1)), + Value::Number(Number::new(2f32)), + ]); + assert_eq!(ron::to_string(&seq).unwrap(), "[1,2.0]"); + assert_eq!("[1, 2.0]".parse(), Ok(seq)); let err = Value::Seq(vec![Value::Number(Number::new(1))]) .into_rust::<[i32; 2]>() @@ -115,12 +161,14 @@ fn unit() { position: Position { col: 1, line: 1 } }) ); + + assert_eq!(ron::to_string(&Value::Unit).unwrap(), "()"); } -#[derive(Serialize)] +#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] struct Scene(Option<(u32, u32)>); -#[derive(Serialize)] +#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] struct Scene2 { foo: Option<(u32, u32)>, } @@ -130,19 +178,33 @@ fn roundtrip() { use ron::{de::from_str, ser::to_string}; { - let s = to_string(&Scene2 { + let v = Scene2 { foo: Some((122, 13)), - }) - .unwrap(); + }; + let s = to_string(&v).unwrap(); println!("{}", s); - let scene: Value = from_str(&s).unwrap(); - println!("{:?}", scene); + let val: Value = from_str(&s).unwrap(); + println!("{:?}", val); + let v2 = val.into_rust::(); + assert_eq!(v2, Ok(v)); } { - let s = to_string(&Scene(Some((13, 122)))).unwrap(); + let v = Scene(Some((13, 122))); + let s = to_string(&v).unwrap(); println!("{}", s); - let scene: Value = from_str(&s).unwrap(); - println!("{:?}", scene); + let val: Value = from_str(&s).unwrap(); + println!("{:?}", val); + let v2 = val.into_rust::(); + assert_eq!(v2, Ok(v)); + } + { + let v = (42,); + let s = to_string(&v).unwrap(); + println!("{}", s); + let val: Value = from_str(&s).unwrap(); + println!("{:?}", val); + let v2 = val.into_rust::<(i32,)>(); + assert_eq!(v2, Ok(v)); } } diff --git a/third_party/rust/wgpu-core/.cargo-checksum.json b/third_party/rust/wgpu-core/.cargo-checksum.json index 86506c9389e2..bed96058dff1 100644 --- a/third_party/rust/wgpu-core/.cargo-checksum.json +++ b/third_party/rust/wgpu-core/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"8b7080de8ce33fedd0eed7f7a4c09b32d61ebf0dd3ff5d703060dc376bba2efe","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","build.rs":"c67295643e006849b11d2d4bf64142e6485280e19887523785c3c03cb5fe1f32","src/binding_model.rs":"29351db199a1f8e475489fb663122c39f04c1e4f9a73ec6a996b1903b7291c7e","src/command/allocator.rs":"386cb6e60bd332a881dbbe57ff66a0fa83f35e3ee924559f1689418ac6c7273a","src/command/bind.rs":"71b86e0db5cfaa2b0a4b849320173135893e90d63763c3002a60dcc8fc415a89","src/command/bundle.rs":"cee52e3bce74497b06c733d5c78d39b315124085cd203b0452d52d51d00fc873","src/command/clear.rs":"4522488370bd90b5264e1ddfcacd6d8c48256b5305fa89403e518401ec6970e3","src/command/compute.rs":"ceb91f4922d6b9bde7ac1e4c74ba3880e5efc6f13a9c905aa8c4f8116beda24d","src/command/compute_command.rs":"18aa0b8e389a5d345243b876b1abbacfc998a19d23069e092183fa7be10fa0ab","src/command/draw.rs":"fe23039c3918eb4e61b9c1ac12715eee448277bb4d3537caa4f11287c04687af","src/command/memory_init.rs":"f25554cff06f96e37afd81153a351e9d8482c855318b86b4db29231926d265b5","src/command/mod.rs":"b02acf1aac4d35b31ae643da2f4a367a94bdf5f513b07169918e8a926bba79ce","src/command/query.rs":"0f6e1ead307465318977a6f3c09a3b902b9e59f7a38fcb08db421c5eab57f068","src/command/ray_tracing.rs":"d29e9afe9f7285c9a553d91596ce976c2f4bcd033188c98567fb325e6c6d4307","src/command/render.rs":"6eda7f40fcf2332a30f1fdf361d3cf51d9d3350509300c9f030738ae181ddca4","src/command/render_command.rs":"f216e42d27a6bef9d2e024881f978a11411d07bf2d771262ba79fb1c1e2842d8","src/command/timestamp_writes.rs":"da06fb07b8d0917be3e7fb9212d0ffc6b16526e1c1df617213ef354c3e1fb446","src/command/transfer.rs":"cbc0cbbeaf7f59913ca72cb01bebcd5987674b1b0e58022fba207afde9342547","src/command/transition_resources.rs":"1824f6155047af4b19aeceaa72ac53f1289270eb2732c9f7f3574d806d62f206","src/conv.rs":"e922723428b0ebd774dd52704838a20085b87b8b44ee666485abdce3a9f07fac","src/device/bgl.rs":"fcb1d53b692970912781748379df675268981c97352872abf2e7bb0b1ebdd533","src/device/global.rs":"845fcab531a92eaebdfdc6f71a42fdb81c2884aafe1818c43601e7982f3e3fb0","src/device/life.rs":"bc1899dbf8d2b0389cba39775cc47d5dd0e2ae3a250b5e9db3dd79d613e4fd18","src/device/mod.rs":"4e4e50cae954a5240a8e3cad8644f9cd9be90db81eb1ea635f218adf12f23e6c","src/device/queue.rs":"0c4697c841f351355246b99931ec9b7d1836a1ed946f885942dbb5954b60d981","src/device/ray_tracing.rs":"72dab8c0998ef92cad3a0a8ba62675dbdee2feb2044a78079510f9ab1c2a9742","src/device/resource.rs":"444c5e0fc3db7aa5b2d61aa665234a3cb8fdd99456ca190b4b1940ffc8e4e97d","src/device/trace.rs":"fc817998af3a0da99820ef9d58c3e9f70c8456c0c472f95ea780789cff6964c8","src/error.rs":"4f07a0b09b30b2d6cbc855d0091d724000f492018af3b41e80befbeccf2a6f4e","src/global.rs":"7bb7dc795d01607fd5d6f4477b55255c71fe4b1f8621a004b0a4305d6641fc20","src/hal_api.rs":"7962c119456602e9c2ba958712660532290cabe0b8c4c67ef7595de73cdf8b57","src/hash_utils.rs":"9f57a627fe3f00659391670141df62f68d67922c7b186f2a9a5958ab16fb576f","src/hub.rs":"ce0a14d5a3314df318bbc7c4d8c40ea2b3197dbddb2227e828ceb5127db7d37d","src/id.rs":"207f262558388147b484c51cf07d2554c26a3e19243058939e63a7f8e5c7ea75","src/identity.rs":"0a92302fc5b483ea1a8750b1672971af1cf2abfd4c0325bb02488b43e85073e2","src/indirect_validation.rs":"b41c76f41766dcc4b997e37df3291b10ae8acfe3e9eb5156aaf6cac964ba5f04","src/init_tracker/buffer.rs":"6167a400ab271ba857f1c507e60a46fbd318c185aff87eecf7eb05f7f09b6963","src/init_tracker/mod.rs":"3895c4a2284631b6d6247c0d96c5fc9cfd024d358e09cad8752b2505700138a3","src/init_tracker/texture.rs":"83e3769b21ee7b784f71f638a525bf25daf91da23fee1f807af739cf89c88391","src/instance.rs":"1824acef6313c740176eb5e91f5b6277712b40e37233487de48abed0d10ed7ae","src/lib.rs":"f5ec2dfa1a1c967a88205f86d66251f8e3b5950a6e1a0a5edc9882489ed435cb","src/lock/mod.rs":"7343aa3ee73a0e6d41f0c28c658f22a2af0ff3e558a2c3437764ebc038816c0f","src/lock/observing.rs":"acc4fba523512066a519b95cf1f7846fda08a9d76e1ce65168aac8b21b67d1b6","src/lock/rank.rs":"8cf08cd720bac88c06bd8b0fe11f35175546c3e52c75a0163ddeded6fb383923","src/lock/ranked.rs":"7f44776bc7d71a25e23c97ab80e2fdab3576bb3f912bc8a0b4fcc28a64ef5c6e","src/lock/vanilla.rs":"713c4f8408b60779bb32377aca6fb7fd1241456057ebcf01f7602f52397a7cbe","src/pipeline.rs":"e6d57bb5aca82dc389214899066c6fd644dbae63b89df8c36e92471bc52d4ff1","src/pipeline_cache.rs":"ada909b8d80de15687963559eb04936181d6b50d5ec1ea50a2188721748da4ed","src/pool.rs":"e11bfdc73a66321d26715465fa74657dcd26a9b856b80b8369b4aac2132a4295","src/present.rs":"165c9e0f81c87e6b5c0500915e8f9db0448208a953d2e9468bd270ed2f2948ce","src/ray_tracing.rs":"ba6e9c78e26e28821582b996e2b19aa751f03dea6a84336788e5e31aaeefef8b","src/registry.rs":"779d8c277193537edd2b883b72cdcc07161b7231c8d2c80be726bd800cd327b6","src/resource.rs":"1935a4775b4a93402ddda2c906ce1fb90b015af5489dd8306c94a1b0962341e5","src/scratch.rs":"05f2032fa6d6f589c2507f4028755769b9524029e5ad59cc500cab7940e40d42","src/snatch.rs":"d75583fa0504b60554b14a865e1b1062b6b1dea7d99903cf09d03608f3a78a4c","src/storage.rs":"2ea3695b77ea540fee05149650005043fcc0abb3eb405738b5dea53efa60903d","src/track/buffer.rs":"1ab5310367606fc74f0791733ea5ba2e09acc92ff7f4f4773f66bcfb3138c52f","src/track/metadata.rs":"04b8bcf8ded7c7c805d9336cfc874da9d8de7d12d99f7525f0540780a1dffc45","src/track/mod.rs":"6af2b831b3a0c91a60a8fd6f141db67695218e0af1735a31b9dee39053703b27","src/track/range.rs":"2688b05a0c6e8510ff6ba3a9623e8b83f433a05ba743129928c56c93d9a9c233","src/track/stateless.rs":"3db699f5f48a319fa07fb16cdf51e1623d6ecac7a476467ee366e014ea665b89","src/track/texture.rs":"fbd5f3cde5161404048131dbea014806cfc210ab1497bfd638f0064969c004f9","src/validation.rs":"0a5425428ebe395e8382ec31ab777927288e7fde4b9997a47c67f96ed3e492ff","src/weak_vec.rs":"a4193add5912b91226a3155cc613365b7fafdf2e7929d21d68bc19d149696e85"},"package":null} \ No newline at end of file +{"files":{"Cargo.toml":"d3359a054ca27eeee8721b4e4577d133c6928d331e831fb5d3bcdd65f5314597","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","build.rs":"c67295643e006849b11d2d4bf64142e6485280e19887523785c3c03cb5fe1f32","src/binding_model.rs":"29351db199a1f8e475489fb663122c39f04c1e4f9a73ec6a996b1903b7291c7e","src/command/allocator.rs":"386cb6e60bd332a881dbbe57ff66a0fa83f35e3ee924559f1689418ac6c7273a","src/command/bind.rs":"c88aa87bf3052fe0993b79ff047339fac4557ead85973f4a37bf236fba401222","src/command/bundle.rs":"03ecff7ad679938242cf0b4a16ea273c129e0805e6c462233d80d3767b25bd42","src/command/clear.rs":"4522488370bd90b5264e1ddfcacd6d8c48256b5305fa89403e518401ec6970e3","src/command/compute.rs":"b5521f256037db88926cafefe8bb958af14ef1bd867df5971469a6013acf7b25","src/command/compute_command.rs":"18aa0b8e389a5d345243b876b1abbacfc998a19d23069e092183fa7be10fa0ab","src/command/draw.rs":"fe23039c3918eb4e61b9c1ac12715eee448277bb4d3537caa4f11287c04687af","src/command/memory_init.rs":"f25554cff06f96e37afd81153a351e9d8482c855318b86b4db29231926d265b5","src/command/mod.rs":"ef8b3c4627ca6272a58e10c36bbb203ec804d9a842f8bca3f97900b714eb9b9f","src/command/query.rs":"0f6e1ead307465318977a6f3c09a3b902b9e59f7a38fcb08db421c5eab57f068","src/command/ray_tracing.rs":"d29e9afe9f7285c9a553d91596ce976c2f4bcd033188c98567fb325e6c6d4307","src/command/render.rs":"681da9291263641302f733de49049219890ed051726391182e11ee57dba02e8e","src/command/render_command.rs":"209242e9ab30e23044840f89efed62e4d0dbadf832fcaf5a861d2487af4358ad","src/command/timestamp_writes.rs":"da06fb07b8d0917be3e7fb9212d0ffc6b16526e1c1df617213ef354c3e1fb446","src/command/transfer.rs":"cbc0cbbeaf7f59913ca72cb01bebcd5987674b1b0e58022fba207afde9342547","src/command/transition_resources.rs":"1824f6155047af4b19aeceaa72ac53f1289270eb2732c9f7f3574d806d62f206","src/conv.rs":"e922723428b0ebd774dd52704838a20085b87b8b44ee666485abdce3a9f07fac","src/device/bgl.rs":"fcb1d53b692970912781748379df675268981c97352872abf2e7bb0b1ebdd533","src/device/global.rs":"715a2fb3c353724aa69affcbd3d62df5378860e9dc1e0ff55a758c27d5d11795","src/device/life.rs":"bc1899dbf8d2b0389cba39775cc47d5dd0e2ae3a250b5e9db3dd79d613e4fd18","src/device/mod.rs":"4e4e50cae954a5240a8e3cad8644f9cd9be90db81eb1ea635f218adf12f23e6c","src/device/queue.rs":"286e61fb7ad8d403c17604d4e9e7e9863e058f6bd2422b7fb78d1c2294fe9bd1","src/device/ray_tracing.rs":"72dab8c0998ef92cad3a0a8ba62675dbdee2feb2044a78079510f9ab1c2a9742","src/device/resource.rs":"bdc7aab4e865d6e35fb66345af6cb3247884018a49fa2e59cb7b4ea58b790e1a","src/device/trace.rs":"fc817998af3a0da99820ef9d58c3e9f70c8456c0c472f95ea780789cff6964c8","src/error.rs":"4f07a0b09b30b2d6cbc855d0091d724000f492018af3b41e80befbeccf2a6f4e","src/global.rs":"7bb7dc795d01607fd5d6f4477b55255c71fe4b1f8621a004b0a4305d6641fc20","src/hal_api.rs":"7962c119456602e9c2ba958712660532290cabe0b8c4c67ef7595de73cdf8b57","src/hash_utils.rs":"9f57a627fe3f00659391670141df62f68d67922c7b186f2a9a5958ab16fb576f","src/hub.rs":"ce0a14d5a3314df318bbc7c4d8c40ea2b3197dbddb2227e828ceb5127db7d37d","src/id.rs":"207f262558388147b484c51cf07d2554c26a3e19243058939e63a7f8e5c7ea75","src/identity.rs":"0a92302fc5b483ea1a8750b1672971af1cf2abfd4c0325bb02488b43e85073e2","src/indirect_validation/dispatch.rs":"1d732bb0e0716c6e5505871ea0dcc9de128fb9c9e2813769f359088d8f1573f9","src/indirect_validation/draw.rs":"6a53ad1ce321792f8d8d57df00797daa062f3b2b319ed8e4a69039adc266e698","src/indirect_validation/mod.rs":"1efb1efcdee323a22946f4886ae4c6b308894a99a253bd73f8430f59bab4f06f","src/indirect_validation/utils.rs":"e6a3b636dd71ff6b01d5abd5a589a0136fb30aa1d517a43f45cf2e5ad54da245","src/indirect_validation/validate_draw.wgsl":"87bc9419c87109d716414eab76d86ea25cf6bf6c08d40bcebfc4c5a3a9c0d67c","src/init_tracker/buffer.rs":"6167a400ab271ba857f1c507e60a46fbd318c185aff87eecf7eb05f7f09b6963","src/init_tracker/mod.rs":"3895c4a2284631b6d6247c0d96c5fc9cfd024d358e09cad8752b2505700138a3","src/init_tracker/texture.rs":"ffdc67d4be23bcd48b22945de94ac0ea8ee571f91eb6d00323b9afe6fa91eef3","src/instance.rs":"1824acef6313c740176eb5e91f5b6277712b40e37233487de48abed0d10ed7ae","src/lib.rs":"3527eba2004e2b36ab4925acc22ed9bfdf315ce26bc6a31baed0caca74467829","src/lock/mod.rs":"7343aa3ee73a0e6d41f0c28c658f22a2af0ff3e558a2c3437764ebc038816c0f","src/lock/observing.rs":"890356f2a8cc1fc085a7ddff75486b66024aa09d6b0d6029f011ec9b4fbe70a8","src/lock/rank.rs":"816a3b93fd9585c3cb4e92c8c91c8e1e027cc8af3f4aa5e8959acdfa5d309f2d","src/lock/ranked.rs":"7f44776bc7d71a25e23c97ab80e2fdab3576bb3f912bc8a0b4fcc28a64ef5c6e","src/lock/vanilla.rs":"3a772dfc1f8d8d4047669962beaa15542f771d2dcbe4e1755cd2cfed5cbacca0","src/pipeline.rs":"9d65c780702098b9a15d4fcffe1574a10817f5de03995e4f5619ffc95bbc5480","src/pipeline_cache.rs":"41d0b8faa0ae834f7b1c6a012574d0c4b380e4446bc26f272502ef1455a15e3a","src/pool.rs":"e11bfdc73a66321d26715465fa74657dcd26a9b856b80b8369b4aac2132a4295","src/present.rs":"165c9e0f81c87e6b5c0500915e8f9db0448208a953d2e9468bd270ed2f2948ce","src/ray_tracing.rs":"ba6e9c78e26e28821582b996e2b19aa751f03dea6a84336788e5e31aaeefef8b","src/registry.rs":"779d8c277193537edd2b883b72cdcc07161b7231c8d2c80be726bd800cd327b6","src/resource.rs":"fb9e6d87e636becdd9fc2e11a53a502c14c66f89e4a7e68000a27de4a9e4320d","src/scratch.rs":"05f2032fa6d6f589c2507f4028755769b9524029e5ad59cc500cab7940e40d42","src/snatch.rs":"d75583fa0504b60554b14a865e1b1062b6b1dea7d99903cf09d03608f3a78a4c","src/storage.rs":"2ea3695b77ea540fee05149650005043fcc0abb3eb405738b5dea53efa60903d","src/track/buffer.rs":"1ab5310367606fc74f0791733ea5ba2e09acc92ff7f4f4773f66bcfb3138c52f","src/track/metadata.rs":"04b8bcf8ded7c7c805d9336cfc874da9d8de7d12d99f7525f0540780a1dffc45","src/track/mod.rs":"6af2b831b3a0c91a60a8fd6f141db67695218e0af1735a31b9dee39053703b27","src/track/range.rs":"2688b05a0c6e8510ff6ba3a9623e8b83f433a05ba743129928c56c93d9a9c233","src/track/stateless.rs":"3db699f5f48a319fa07fb16cdf51e1623d6ecac7a476467ee366e014ea665b89","src/track/texture.rs":"fbd5f3cde5161404048131dbea014806cfc210ab1497bfd638f0064969c004f9","src/validation.rs":"a4ec78a9dcc893fdbcb9217102f5e79e85eaba1a5065892d983ed49014da488a","src/weak_vec.rs":"a4193add5912b91226a3155cc613365b7fafdf2e7929d21d68bc19d149696e85"},"package":null} \ No newline at end of file diff --git a/third_party/rust/wgpu-core/Cargo.toml b/third_party/rust/wgpu-core/Cargo.toml index 7ec74da1fcd9..0afce3cfd25b 100644 --- a/third_party/rust/wgpu-core/Cargo.toml +++ b/third_party/rust/wgpu-core/Cargo.toml @@ -56,7 +56,6 @@ gles = [ "wgpu-core-deps-emscripten/gles", ] glsl = ["naga/glsl-in"] -indirect-validation = ["naga/wgsl-in"] metal = ["wgpu-core-deps-apple/metal"] noop = [] observe_locks = [ @@ -94,6 +93,7 @@ trace = [ "dep:ron", "naga/serialize", "wgpu-types/trace", + "dep:bytemuck", ] vulkan = ["wgpu-core-deps-windows-linux-android/vulkan"] vulkan-portability = ["wgpu-core-deps-apple/vulkan-portability"] @@ -108,6 +108,10 @@ path = "src/lib.rs" version = "0.7.6" default-features = false +[dependencies.bit-set] +version = "0.8" +default-features = false + [dependencies.bit-vec] version = "0.8" default-features = false @@ -118,7 +122,6 @@ version = "2.9" [dependencies.bytemuck] version = "1.22" features = [ - "derive", "extern_crate_alloc", "min_const_generics", ] @@ -164,7 +167,7 @@ optional = true default-features = false [dependencies.ron] -version = "0.8" +version = "0.9" optional = true [dependencies.rustc-hash] diff --git a/third_party/rust/wgpu-core/src/command/bind.rs b/third_party/rust/wgpu-core/src/command/bind.rs index 445313214df6..9f6b89ab8c40 100644 --- a/third_party/rust/wgpu-core/src/command/bind.rs +++ b/third_party/rust/wgpu-core/src/command/bind.rs @@ -412,7 +412,6 @@ impl Binder { .map(move |index| payloads[index].group.as_ref().unwrap()) } - #[cfg(feature = "indirect-validation")] pub(super) fn list_valid<'a>(&'a self) -> impl Iterator + 'a { self.payloads .iter() diff --git a/third_party/rust/wgpu-core/src/command/bundle.rs b/third_party/rust/wgpu-core/src/command/bundle.rs index e192c0b99bc5..d1d848f08acc 100644 --- a/third_party/rust/wgpu-core/src/command/bundle.rs +++ b/third_party/rust/wgpu-core/src/command/bundle.rs @@ -784,29 +784,39 @@ fn multi_draw_indirect( let buffer = buffer_guard.get(buffer_id).get()?; - state - .trackers - .buffers - .merge_single(&buffer, wgt::BufferUses::INDIRECT)?; - buffer.same_device(&state.device)?; buffer.check_usage(wgt::BufferUsages::INDIRECT)?; + let vertex_limits = super::VertexLimits::new(state.vertex_buffer_sizes(), &pipeline.steps); + + let stride = super::get_stride_of_indirect_args(indexed); state .buffer_memory_init_actions .extend(buffer.initialization_status.read().create_action( &buffer, - offset..(offset + size_of::() as u64), + offset..(offset + stride), MemoryInitKind::NeedsInitializedMemory, )); - if indexed { + let vertex_or_index_limit = if indexed { let index = match state.index { Some(ref mut index) => index, None => return Err(DrawError::MissingIndexBuffer.into()), }; state.commands.extend(index.flush()); - } + index.limit() + } else { + vertex_limits.vertex_limit + }; + let instance_limit = vertex_limits.instance_limit; + + let buffer_uses = if state.device.indirect_validation.is_some() { + wgt::BufferUses::STORAGE_READ_ONLY + } else { + wgt::BufferUses::INDIRECT + }; + + state.trackers.buffers.merge_single(&buffer, buffer_uses)?; state.flush_vertices(); state.flush_binds(used_bind_groups, dynamic_offsets); @@ -815,6 +825,9 @@ fn multi_draw_indirect( offset, count: 1, indexed, + + vertex_or_index_limit, + instance_limit, }); Ok(()) } @@ -833,6 +846,8 @@ pub enum CreateRenderBundleError { #[derive(Clone, Debug, Error)] #[non_exhaustive] pub enum ExecutionError { + #[error(transparent)] + Device(#[from] DeviceError), #[error(transparent)] DestroyedResource(#[from] DestroyedResourceError), #[error("Using {0} in a render bundle is not implemented")] @@ -886,6 +901,8 @@ impl RenderBundle { pub(super) unsafe fn execute( &self, raw: &mut dyn hal::DynCommandEncoder, + indirect_draw_validation_resources: &mut crate::indirect_validation::DrawResources, + indirect_draw_validation_batcher: &mut crate::indirect_validation::DrawBatcher, snatch_guard: &SnatchGuard, ) -> Result<(), ExecutionError> { let mut offsets = self.base.dynamic_offsets.as_slice(); @@ -1028,19 +1045,33 @@ impl RenderBundle { buffer, offset, count: 1, - indexed: false, + indexed, + + vertex_or_index_limit, + instance_limit, } => { - let buffer = buffer.try_raw(snatch_guard)?; - unsafe { raw.draw_indirect(buffer, *offset, 1) }; - } - Cmd::DrawIndirect { - buffer, - offset, - count: 1, - indexed: true, - } => { - let buffer = buffer.try_raw(snatch_guard)?; - unsafe { raw.draw_indexed_indirect(buffer, *offset, 1) }; + let (buffer, offset) = if self.device.indirect_validation.is_some() { + let (dst_resource_index, offset) = indirect_draw_validation_batcher.add( + indirect_draw_validation_resources, + &self.device, + buffer, + *offset, + *indexed, + *vertex_or_index_limit, + *instance_limit, + )?; + + let dst_buffer = + indirect_draw_validation_resources.get_dst_buffer(dst_resource_index); + (dst_buffer, offset) + } else { + (buffer.try_raw(snatch_guard)?, *offset) + }; + if *indexed { + unsafe { raw.draw_indexed_indirect(buffer, offset, 1) }; + } else { + unsafe { raw.draw_indirect(buffer, offset, 1) }; + } } Cmd::DrawIndirect { .. } | Cmd::MultiDrawIndirectCount { .. } => { return Err(ExecutionError::Unimplemented("multi-draw-indirect")) diff --git a/third_party/rust/wgpu-core/src/command/compute.rs b/third_party/rust/wgpu-core/src/command/compute.rs index 8519e8be82b6..e8e0a9688a31 100644 --- a/third_party/rust/wgpu-core/src/command/compute.rs +++ b/third_party/rust/wgpu-core/src/command/compute.rs @@ -765,7 +765,7 @@ fn set_pipeline( { // Note that non-0 range start doesn't work anyway https://github.com/gfx-rs/wgpu/issues/4502 let len = push_constant_range.len() / wgt::PUSH_CONSTANT_ALIGNMENT as usize; - state.push_constants.extend(core::iter::repeat(0).take(len)); + state.push_constants.extend(core::iter::repeat_n(0, len)); } // Clear push constant ranges @@ -891,13 +891,10 @@ fn dispatch_indirect( MemoryInitKind::NeedsInitializedMemory, )); - #[cfg(feature = "indirect-validation")] - { - let params = state.device.indirect_validation.as_ref().unwrap().params( - &state.device.limits, - offset, - buffer.size, - ); + if let Some(ref indirect_validation) = state.device.indirect_validation { + let params = indirect_validation + .dispatch + .params(&state.device.limits, offset, buffer.size); unsafe { state.raw_encoder.set_compute_pipeline(params.pipeline); @@ -926,9 +923,10 @@ fn dispatch_indirect( 1, Some( buffer - .raw_indirect_validation_bind_group + .indirect_validation_bind_groups .get(&state.snatch_guard) .unwrap() + .dispatch .as_ref(), ), &[params.aligned_offset as u32], @@ -1006,9 +1004,7 @@ fn dispatch_indirect( unsafe { state.raw_encoder.dispatch_indirect(params.dst_buffer, 0); } - }; - #[cfg(not(feature = "indirect-validation"))] - { + } else { state .scope .buffers diff --git a/third_party/rust/wgpu-core/src/command/mod.rs b/third_party/rust/wgpu-core/src/command/mod.rs index abc8043732d6..ace017e397ee 100644 --- a/third_party/rust/wgpu-core/src/command/mod.rs +++ b/third_party/rust/wgpu-core/src/command/mod.rs @@ -436,6 +436,7 @@ pub(crate) struct BakedCommands { pub(crate) encoder: CommandEncoder, pub(crate) trackers: Tracker, pub(crate) temp_resources: Vec, + pub(crate) indirect_draw_validation_resources: crate::indirect_validation::DrawResources, buffer_memory_init_actions: Vec, texture_memory_actions: CommandBufferTextureMemoryActions, } @@ -466,6 +467,8 @@ pub struct CommandBufferMutable { tlas_actions: Vec, temp_resources: Vec, + indirect_draw_validation_resources: crate::indirect_validation::DrawResources, + #[cfg(feature = "trace")] pub(crate) commands: Option>, } @@ -485,6 +488,7 @@ impl CommandBufferMutable { encoder: self.encoder, trackers: self.trackers, temp_resources: self.temp_resources, + indirect_draw_validation_resources: self.indirect_draw_validation_resources, buffer_memory_init_actions: self.buffer_memory_init_actions, texture_memory_actions: self.texture_memory_actions, } @@ -552,6 +556,8 @@ impl CommandBuffer { blas_actions: Default::default(), tlas_actions: Default::default(), temp_resources: Default::default(), + indirect_draw_validation_resources: + crate::indirect_validation::DrawResources::new(device.clone()), #[cfg(feature = "trace")] commands: if device.trace.lock().is_some() { Some(Vec::new()) diff --git a/third_party/rust/wgpu-core/src/command/render.rs b/third_party/rust/wgpu-core/src/command/render.rs index 6d59f1b18688..72bb2fab6aac 100644 --- a/third_party/rust/wgpu-core/src/command/render.rs +++ b/third_party/rust/wgpu-core/src/command/render.rs @@ -1631,6 +1631,8 @@ impl Global { let device = &cmd_buf.device; let snatch_guard = &device.snatchable_lock.read(); + let mut indirect_draw_validation_batcher = crate::indirect_validation::DrawBatcher::new(); + let (scope, pending_discard_init_fixups) = { device.check_is_valid().map_pass_err(pass_scope)?; @@ -1639,6 +1641,8 @@ impl Global { let buffer_memory_init_actions = &mut cmd_buf_data.buffer_memory_init_actions; let texture_memory_actions = &mut cmd_buf_data.texture_memory_actions; let pending_query_resets = &mut cmd_buf_data.pending_query_resets; + let indirect_draw_validation_resources = + &mut cmd_buf_data.indirect_draw_validation_resources; // We automatically keep extending command buffers over time, and because // we want to insert a command buffer _before_ what we're about to record, @@ -1819,6 +1823,9 @@ impl Global { offset, count, indexed, + + vertex_or_index_limit: _, + instance_limit: _, } => { let scope = PassErrorScope::Draw { kind: if count != 1 { @@ -1828,8 +1835,17 @@ impl Global { }, indexed, }; - multi_draw_indirect(&mut state, cmd_buf, buffer, offset, count, indexed) - .map_pass_err(scope)?; + multi_draw_indirect( + &mut state, + indirect_draw_validation_resources, + &mut indirect_draw_validation_batcher, + cmd_buf, + buffer, + offset, + count, + indexed, + ) + .map_pass_err(scope)?; } ArcRenderCommand::MultiDrawIndirectCount { buffer, @@ -1939,7 +1955,14 @@ impl Global { } ArcRenderCommand::ExecuteBundle(bundle) => { let scope = PassErrorScope::ExecuteBundle; - execute_bundle(&mut state, cmd_buf, bundle).map_pass_err(scope)?; + execute_bundle( + &mut state, + indirect_draw_validation_resources, + &mut indirect_draw_validation_batcher, + cmd_buf, + bundle, + ) + .map_pass_err(scope)?; } } } @@ -1972,6 +1995,20 @@ impl Global { cmd_buf_data.pending_query_resets.reset_queries(transit); CommandBuffer::insert_barriers_from_scope(transit, tracker, &scope, snatch_guard); + + if let Some(ref indirect_validation) = device.indirect_validation { + indirect_validation + .draw + .inject_validation_pass( + device, + snatch_guard, + &mut cmd_buf_data.indirect_draw_validation_resources, + &mut cmd_buf_data.temp_resources, + transit, + indirect_draw_validation_batcher, + ) + .map_pass_err(pass_scope)?; + } } encoder.close_and_swap().map_pass_err(pass_scope)?; @@ -2457,6 +2494,8 @@ fn draw_indexed( fn multi_draw_indirect( state: &mut State, + indirect_draw_validation_resources: &mut crate::indirect_validation::DrawResources, + indirect_draw_validation_batcher: &mut crate::indirect_validation::DrawBatcher, cmd_buf: &Arc, indirect_buffer: Arc, offset: u64, @@ -2470,36 +2509,26 @@ fn multi_draw_indirect( state.is_ready(indexed)?; - let stride = match indexed { - false => size_of::(), - true => size_of::(), - }; - if count != 1 { state .device .require_features(wgt::Features::MULTI_DRAW_INDIRECT)?; } + state .device .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?; indirect_buffer.same_device_as(cmd_buf.as_ref())?; - - state - .info - .usage_scope - .buffers - .merge_single(&indirect_buffer, wgt::BufferUses::INDIRECT)?; - indirect_buffer.check_usage(BufferUsages::INDIRECT)?; - let indirect_raw = indirect_buffer.try_raw(state.snatch_guard)?; if offset % 4 != 0 { return Err(RenderPassErrorInner::UnalignedIndirectBufferOffset(offset)); } - let end_offset = offset + stride as u64 * count as u64; + let stride = get_stride_of_indirect_args(indexed); + + let end_offset = offset + stride * count as u64; if end_offset > indirect_buffer.size { return Err(RenderPassErrorInner::IndirectBufferOverrun { count, @@ -2517,16 +2546,129 @@ fn multi_draw_indirect( ), ); - match indexed { - false => unsafe { - state.raw_encoder.draw_indirect(indirect_raw, offset, count); - }, - true => unsafe { - state - .raw_encoder - .draw_indexed_indirect(indirect_raw, offset, count); - }, + fn draw( + raw_encoder: &mut dyn hal::DynCommandEncoder, + indexed: bool, + indirect_buffer: &dyn hal::DynBuffer, + offset: u64, + count: u32, + ) { + match indexed { + false => unsafe { + raw_encoder.draw_indirect(indirect_buffer, offset, count); + }, + true => unsafe { + raw_encoder.draw_indexed_indirect(indirect_buffer, offset, count); + }, + } } + + if state.device.indirect_validation.is_some() { + state + .info + .usage_scope + .buffers + .merge_single(&indirect_buffer, wgt::BufferUses::STORAGE_READ_ONLY)?; + + struct DrawData { + buffer_index: usize, + offset: u64, + count: u32, + } + + struct DrawContext<'a> { + raw_encoder: &'a mut dyn hal::DynCommandEncoder, + device: &'a Device, + + indirect_draw_validation_resources: &'a mut crate::indirect_validation::DrawResources, + indirect_draw_validation_batcher: &'a mut crate::indirect_validation::DrawBatcher, + + indirect_buffer: Arc, + indexed: bool, + vertex_or_index_limit: u64, + instance_limit: u64, + } + + impl<'a> DrawContext<'a> { + fn add(&mut self, offset: u64) -> Result { + let (dst_resource_index, dst_offset) = self.indirect_draw_validation_batcher.add( + self.indirect_draw_validation_resources, + self.device, + &self.indirect_buffer, + offset, + self.indexed, + self.vertex_or_index_limit, + self.instance_limit, + )?; + Ok(DrawData { + buffer_index: dst_resource_index, + offset: dst_offset, + count: 1, + }) + } + fn draw(&mut self, draw_data: DrawData) { + let dst_buffer = self + .indirect_draw_validation_resources + .get_dst_buffer(draw_data.buffer_index); + draw( + self.raw_encoder, + self.indexed, + dst_buffer, + draw_data.offset, + draw_data.count, + ); + } + } + + let mut draw_ctx = DrawContext { + raw_encoder: state.raw_encoder, + device: state.device, + indirect_draw_validation_resources, + indirect_draw_validation_batcher, + indirect_buffer, + indexed, + vertex_or_index_limit: if indexed { + state.index.limit + } else { + state.vertex.limits.vertex_limit + }, + instance_limit: state.vertex.limits.instance_limit, + }; + + let mut current_draw_data = draw_ctx.add(offset)?; + + for i in 1..count { + let draw_data = draw_ctx.add(offset + stride * i as u64)?; + + if draw_data.buffer_index == current_draw_data.buffer_index { + debug_assert_eq!( + draw_data.offset, + current_draw_data.offset + stride * current_draw_data.count as u64 + ); + current_draw_data.count += 1; + } else { + draw_ctx.draw(current_draw_data); + current_draw_data = draw_data; + } + } + + draw_ctx.draw(current_draw_data); + } else { + state + .info + .usage_scope + .buffers + .merge_single(&indirect_buffer, wgt::BufferUses::INDIRECT)?; + + draw( + state.raw_encoder, + indexed, + indirect_buffer.try_raw(state.snatch_guard)?, + offset, + count, + ); + }; + Ok(()) } @@ -2548,10 +2690,7 @@ fn multi_draw_indirect_count( state.is_ready(indexed)?; - let stride = match indexed { - false => size_of::(), - true => size_of::(), - } as u64; + let stride = get_stride_of_indirect_args(indexed); state .device @@ -2725,6 +2864,8 @@ fn write_timestamp( fn execute_bundle( state: &mut State, + indirect_draw_validation_resources: &mut crate::indirect_validation::DrawResources, + indirect_draw_validation_batcher: &mut crate::indirect_validation::DrawBatcher, cmd_buf: &Arc, bundle: Arc, ) -> Result<(), RenderPassErrorInner> { @@ -2774,9 +2915,22 @@ fn execute_bundle( .extend(state.texture_memory_actions.register_init_action(action)); } - unsafe { bundle.execute(state.raw_encoder, state.snatch_guard) }.map_err(|e| match e { - ExecutionError::DestroyedResource(e) => RenderCommandError::DestroyedResource(e), - ExecutionError::Unimplemented(what) => RenderCommandError::Unimplemented(what), + unsafe { + bundle.execute( + state.raw_encoder, + indirect_draw_validation_resources, + indirect_draw_validation_batcher, + state.snatch_guard, + ) + } + .map_err(|e| match e { + ExecutionError::Device(e) => RenderPassErrorInner::Device(e), + ExecutionError::DestroyedResource(e) => { + RenderPassErrorInner::RenderCommand(RenderCommandError::DestroyedResource(e)) + } + ExecutionError::Unimplemented(what) => { + RenderPassErrorInner::RenderCommand(RenderCommandError::Unimplemented(what)) + } })?; unsafe { @@ -3097,6 +3251,9 @@ impl Global { offset, count: 1, indexed: false, + + vertex_or_index_limit: 0, + instance_limit: 0, }); Ok(()) @@ -3119,6 +3276,9 @@ impl Global { offset, count: 1, indexed: true, + + vertex_or_index_limit: 0, + instance_limit: 0, }); Ok(()) @@ -3142,6 +3302,9 @@ impl Global { offset, count, indexed: false, + + vertex_or_index_limit: 0, + instance_limit: 0, }); Ok(()) @@ -3165,6 +3328,9 @@ impl Global { offset, count, indexed: true, + + vertex_or_index_limit: 0, + instance_limit: 0, }); Ok(()) @@ -3371,3 +3537,10 @@ impl Global { Ok(()) } } + +pub(crate) const fn get_stride_of_indirect_args(indexed: bool) -> u64 { + match indexed { + false => size_of::() as u64, + true => size_of::() as u64, + } +} diff --git a/third_party/rust/wgpu-core/src/command/render_command.rs b/third_party/rust/wgpu-core/src/command/render_command.rs index b7fa73915752..6fc4cbf5cf51 100644 --- a/third_party/rust/wgpu-core/src/command/render_command.rs +++ b/third_party/rust/wgpu-core/src/command/render_command.rs @@ -333,6 +333,9 @@ impl RenderCommand { offset, count, indexed, + + vertex_or_index_limit: 0, + instance_limit: 0, }, RenderCommand::MultiDrawIndirectCount { @@ -464,6 +467,11 @@ pub enum ArcRenderCommand { offset: BufferAddress, count: u32, indexed: bool, + + /// This limit is only populated for commands in a [`RenderBundle`]. + vertex_or_index_limit: u64, + /// This limit is only populated for commands in a [`RenderBundle`]. + instance_limit: u64, }, MultiDrawIndirectCount { buffer: Arc, diff --git a/third_party/rust/wgpu-core/src/device/global.rs b/third_party/rust/wgpu-core/src/device/global.rs index 5b56fe99c46a..af928dfb05fe 100644 --- a/third_party/rust/wgpu-core/src/device/global.rs +++ b/third_party/rust/wgpu-core/src/device/global.rs @@ -964,9 +964,7 @@ impl Global { #[cfg(feature = "trace")] if let Some(ref mut trace) = *device.trace.lock() { - let data = trace.make_binary("spv", unsafe { - core::slice::from_raw_parts(source.as_ptr().cast::(), source.len() * 4) - }); + let data = trace.make_binary("spv", bytemuck::cast_slice(&source)); trace.add(trace::Action::CreateShaderModule { id: fid.id(), desc: desc.clone(), @@ -2190,10 +2188,8 @@ impl Global { let range_size = if let Some(size) = size { size - } else if offset > buffer.size { - 0 } else { - buffer.size - offset + buffer.size.saturating_sub(offset) }; if offset % wgt::MAP_ALIGNMENT != 0 { diff --git a/third_party/rust/wgpu-core/src/device/queue.rs b/third_party/rust/wgpu-core/src/device/queue.rs index e19f9bc5f56a..d78299cbcc7b 100644 --- a/third_party/rust/wgpu-core/src/device/queue.rs +++ b/third_party/rust/wgpu-core/src/device/queue.rs @@ -267,6 +267,8 @@ pub(crate) struct EncoderInFlight { inner: crate::command::CommandEncoder, pub(crate) trackers: Tracker, pub(crate) temp_resources: Vec, + /// We only need to keep these resources alive. + _indirect_draw_validation_resources: crate::indirect_validation::DrawResources, /// These are the buffers that have been tracked by `PendingWrites`. pub(crate) pending_buffers: FastHashMap>, @@ -377,6 +379,9 @@ impl PendingWrites { }, trackers: Tracker::new(), temp_resources: mem::take(&mut self.temp_resources), + _indirect_draw_validation_resources: crate::indirect_validation::DrawResources::new( + device.clone(), + ), pending_buffers, pending_textures, }; @@ -1196,6 +1201,8 @@ impl Queue { inner: baked.encoder, trackers: baked.trackers, temp_resources: baked.temp_resources, + _indirect_draw_validation_resources: baked + .indirect_draw_validation_resources, pending_buffers: FastHashMap::default(), pending_textures: FastHashMap::default(), }); diff --git a/third_party/rust/wgpu-core/src/device/resource.rs b/third_party/rust/wgpu-core/src/device/resource.rs index 83fbcc3327d8..b0296642708e 100644 --- a/third_party/rust/wgpu-core/src/device/resource.rs +++ b/third_party/rust/wgpu-core/src/device/resource.rs @@ -129,7 +129,6 @@ pub struct Device { pub(crate) deferred_destroy: Mutex>, pub(crate) usage_scopes: UsageScopePool, pub(crate) last_acceleration_structure_build_command_index: AtomicU64, - #[cfg(feature = "indirect-validation")] pub(crate) indirect_validation: Option, // needs to be dropped last #[cfg(feature = "trace")] @@ -160,7 +159,6 @@ impl Drop for Device { let zero_buffer = unsafe { ManuallyDrop::take(&mut self.zero_buffer) }; // SAFETY: We are in the Drop impl and we don't use self.fence anymore after this point. let fence = unsafe { ManuallyDrop::take(&mut self.fence.write()) }; - #[cfg(feature = "indirect-validation")] if let Some(indirect_validation) = self.indirect_validation.take() { indirect_validation.dispose(self.raw.as_ref()); } @@ -248,21 +246,18 @@ impl Device { let alignments = adapter.raw.capabilities.alignments.clone(); let downlevel = adapter.raw.capabilities.downlevel.clone(); - #[cfg(feature = "indirect-validation")] - let indirect_validation = if downlevel - .flags - .contains(wgt::DownlevelFlags::INDIRECT_EXECUTION) - { - match crate::indirect_validation::IndirectValidation::new( + let enable_indirect_validation = instance_flags + .contains(wgt::InstanceFlags::VALIDATION_INDIRECT_CALL) + && downlevel + .flags + .contains(wgt::DownlevelFlags::INDIRECT_EXECUTION); + + let indirect_validation = if enable_indirect_validation { + Some(crate::indirect_validation::IndirectValidation::new( raw_device.as_ref(), &desc.required_limits, - ) { - Ok(indirect_validation) => Some(indirect_validation), - Err(e) => { - log::error!("indirect-validation error: {e:?}"); - return Err(DeviceError::Lost); - } - } + &desc.required_features, + )?) } else { None }; @@ -312,7 +307,6 @@ impl Device { usage_scopes: Mutex::new(rank::DEVICE_USAGE_SCOPES, Default::default()), // By starting at one, we can put the result in a NonZeroU64. last_acceleration_structure_build_command_index: AtomicU64::new(1), - #[cfg(feature = "indirect-validation")] indirect_validation, }) } @@ -651,9 +645,8 @@ impl Device { let buffer = unsafe { self.raw().create_buffer(&hal_desc) }.map_err(|e| self.handle_hal_error(e))?; - #[cfg(feature = "indirect-validation")] - let raw_indirect_validation_bind_group = - self.create_indirect_validation_bind_group(buffer.as_ref(), desc.size, desc.usage)?; + let indirect_validation_bind_groups = + self.create_indirect_validation_bind_groups(buffer.as_ref(), desc.size, desc.usage)?; let buffer = Buffer { raw: Snatchable::new(buffer), @@ -668,8 +661,7 @@ impl Device { label: desc.label.to_string(), tracking_data: TrackingData::new(self.tracker_indices.buffers.clone()), bind_groups: Mutex::new(rank::BUFFER_BIND_GROUPS, WeakVec::new()), - #[cfg(feature = "indirect-validation")] - raw_indirect_validation_bind_group, + indirect_validation_bind_groups, }; let buffer = Arc::new(buffer); @@ -751,8 +743,7 @@ impl Device { hal_buffer: Box, desc: &resource::BufferDescriptor, ) -> (Fallible, Option) { - #[cfg(feature = "indirect-validation")] - let raw_indirect_validation_bind_group = match self.create_indirect_validation_bind_group( + let indirect_validation_bind_groups = match self.create_indirect_validation_bind_groups( hal_buffer.as_ref(), desc.size, desc.usage, @@ -776,8 +767,7 @@ impl Device { label: desc.label.to_string(), tracking_data: TrackingData::new(self.tracker_indices.buffers.clone()), bind_groups: Mutex::new(rank::BUFFER_BIND_GROUPS, WeakVec::new()), - #[cfg(feature = "indirect-validation")] - raw_indirect_validation_bind_group, + indirect_validation_bind_groups, }; let buffer = Arc::new(buffer); @@ -790,22 +780,31 @@ impl Device { (Fallible::Valid(buffer), None) } - #[cfg(feature = "indirect-validation")] - fn create_indirect_validation_bind_group( + fn create_indirect_validation_bind_groups( &self, raw_buffer: &dyn hal::DynBuffer, buffer_size: u64, usage: wgt::BufferUsages, - ) -> Result>, resource::CreateBufferError> { - if usage.contains(wgt::BufferUsages::INDIRECT) { - let indirect_validation = self.indirect_validation.as_ref().unwrap(); - let bind_group = indirect_validation - .create_src_bind_group(self.raw(), &self.limits, buffer_size, raw_buffer) - .map_err(resource::CreateBufferError::IndirectValidationBindGroup)?; - match bind_group { - Some(bind_group) => Ok(Snatchable::new(bind_group)), - None => Ok(Snatchable::empty()), - } + ) -> Result, resource::CreateBufferError> + { + if !usage.contains(wgt::BufferUsages::INDIRECT) { + return Ok(Snatchable::empty()); + } + + let Some(ref indirect_validation) = self.indirect_validation else { + return Ok(Snatchable::empty()); + }; + + let bind_groups = crate::indirect_validation::BindGroups::new( + indirect_validation, + self, + buffer_size, + raw_buffer, + ) + .map_err(resource::CreateBufferError::IndirectValidationBindGroup)?; + + if let Some(bind_groups) = bind_groups { + Ok(Snatchable::new(bind_groups)) } else { Ok(Snatchable::empty()) } @@ -2762,7 +2761,7 @@ impl Device { .map(|bgl| bgl.raw()) .collect::>(); - let additional_flags = if cfg!(feature = "indirect-validation") { + let additional_flags = if self.indirect_validation.is_some() { hal::PipelineLayoutFlags::INDIRECT_BUILTIN_UPDATE } else { hal::PipelineLayoutFlags::empty() diff --git a/third_party/rust/wgpu-core/src/indirect_validation.rs b/third_party/rust/wgpu-core/src/indirect_validation/dispatch.rs similarity index 96% rename from third_party/rust/wgpu-core/src/indirect_validation.rs rename to third_party/rust/wgpu-core/src/indirect_validation/dispatch.rs index 624ad3f9ece7..00e3798e9ba9 100644 --- a/third_party/rust/wgpu-core/src/indirect_validation.rs +++ b/third_party/rust/wgpu-core/src/indirect_validation/dispatch.rs @@ -1,23 +1,10 @@ -use alloc::{boxed::Box, format, string::ToString as _}; -use core::num::NonZeroU64; - -use thiserror::Error; - +use super::CreateIndirectValidationPipelineError; use crate::{ device::DeviceError, pipeline::{CreateComputePipelineError, CreateShaderModuleError}, }; - -#[derive(Clone, Debug, Error)] -#[non_exhaustive] -pub enum CreateDispatchIndirectValidationPipelineError { - #[error(transparent)] - DeviceError(#[from] DeviceError), - #[error(transparent)] - ShaderModule(#[from] CreateShaderModuleError), - #[error(transparent)] - ComputePipeline(#[from] CreateComputePipelineError), -} +use alloc::{boxed::Box, format, string::ToString as _}; +use core::num::NonZeroU64; /// This machinery requires the following limits: /// @@ -31,7 +18,7 @@ pub enum CreateDispatchIndirectValidationPipelineError { /// These are all indirectly satisfied by `DownlevelFlags::INDIRECT_EXECUTION`, which is also /// required for this module's functionality to work. #[derive(Debug)] -pub struct IndirectValidation { +pub(crate) struct Dispatch { module: Box, dst_bind_group_layout: Box, src_bind_group_layout: Box, @@ -50,11 +37,11 @@ pub struct Params<'a> { pub offset_remainder: u64, } -impl IndirectValidation { - pub fn new( +impl Dispatch { + pub(super) fn new( device: &dyn hal::DynDevice, limits: &wgt::Limits, - ) -> Result { + ) -> Result { let max_compute_workgroups_per_dimension = limits.max_compute_workgroups_per_dimension; let src = format!( @@ -96,6 +83,7 @@ impl IndirectValidation { ) }; + #[cfg(feature = "wgsl")] let module = naga::front::wgsl::parse_str(&src).map_err(|inner| { CreateShaderModuleError::Parsing(naga::error::ShaderError { source: src.clone(), @@ -103,6 +91,10 @@ impl IndirectValidation { inner: Box::new(inner), }) })?; + #[cfg(not(feature = "wgsl"))] + #[allow(clippy::diverging_sub_expression)] + let module = panic!("Indirect validation requires the wgsl feature flag to be enabled!"); + let info = crate::device::create_validator( wgt::Features::PUSH_CONSTANTS, wgt::DownlevelFlags::empty(), @@ -267,7 +259,7 @@ impl IndirectValidation { } /// `Ok(None)` will only be returned if `buffer_size` is `0`. - pub fn create_src_bind_group( + pub(super) fn create_src_bind_group( &self, device: &dyn hal::DynDevice, limits: &wgt::Limits, @@ -336,8 +328,8 @@ impl IndirectValidation { } } - pub fn dispose(self, device: &dyn hal::DynDevice) { - let IndirectValidation { + pub(super) fn dispose(self, device: &dyn hal::DynDevice) { + let Dispatch { module, dst_bind_group_layout, src_bind_group_layout, diff --git a/third_party/rust/wgpu-core/src/indirect_validation/draw.rs b/third_party/rust/wgpu-core/src/indirect_validation/draw.rs new file mode 100644 index 000000000000..4685db39b432 --- /dev/null +++ b/third_party/rust/wgpu-core/src/indirect_validation/draw.rs @@ -0,0 +1,961 @@ +use super::{ + utils::{BufferBarrierScratch, BufferBarriers, UniqueIndexExt as _, UniqueIndexScratch}, + CreateIndirectValidationPipelineError, +}; +use crate::{ + device::{queue::TempResource, Device, DeviceError}, + lock::{rank, Mutex}, + pipeline::{CreateComputePipelineError, CreateShaderModuleError}, + resource::{StagingBuffer, Trackable}, + snatch::SnatchGuard, + track::TrackerIndex, + FastHashMap, +}; +use alloc::{boxed::Box, string::ToString, sync::Arc, vec, vec::Vec}; +use core::{ + mem::{size_of, size_of_val}, + num::NonZeroU64, +}; +use wgt::Limits; + +/// Note: This needs to be under: +/// +/// default max_compute_workgroups_per_dimension * size_of::() * `workgroup_size` used by the shader +/// +/// = (2^16 - 1) * 2^4 * 2^6 +/// +/// It is currently set to: +/// +/// = (2^16 - 1) * 2^4 +/// +/// This is enough space for: +/// +/// - 65535 [`wgt::DrawIndirectArgs`] / [`MetadataEntry`] +/// - 52428 [`wgt::DrawIndexedIndirectArgs`] +const BUFFER_SIZE: wgt::BufferSize = unsafe { wgt::BufferSize::new_unchecked(1_048_560) }; + +/// Holds all device-level resources that are needed to validate indirect draws. +/// +/// This machinery requires the following limits: +/// +/// - max_bind_groups: 3, +/// - max_dynamic_storage_buffers_per_pipeline_layout: 1, +/// - max_storage_buffers_per_shader_stage: 3, +/// - max_push_constant_size: 8, +/// +/// These are all indirectly satisfied by `DownlevelFlags::INDIRECT_EXECUTION`, which is also +/// required for this module's functionality to work. +#[derive(Debug)] +pub(crate) struct Draw { + module: Box, + metadata_bind_group_layout: Box, + src_bind_group_layout: Box, + dst_bind_group_layout: Box, + pipeline_layout: Box, + pipeline: Box, + + free_indirect_entries: Mutex>, + free_metadata_entries: Mutex>, +} + +impl Draw { + pub(super) fn new( + device: &dyn hal::DynDevice, + required_features: &wgt::Features, + ) -> Result { + let module = create_validation_module(device)?; + + let metadata_bind_group_layout = + create_bind_group_layout(device, true, false, BUFFER_SIZE)?; + let src_bind_group_layout = + create_bind_group_layout(device, true, true, wgt::BufferSize::new(4 * 4).unwrap())?; + let dst_bind_group_layout = create_bind_group_layout(device, false, false, BUFFER_SIZE)?; + + let pipeline_layout_desc = hal::PipelineLayoutDescriptor { + label: None, + flags: hal::PipelineLayoutFlags::empty(), + bind_group_layouts: &[ + metadata_bind_group_layout.as_ref(), + src_bind_group_layout.as_ref(), + dst_bind_group_layout.as_ref(), + ], + push_constant_ranges: &[wgt::PushConstantRange { + stages: wgt::ShaderStages::COMPUTE, + range: 0..8, + }], + }; + let pipeline_layout = unsafe { + device + .create_pipeline_layout(&pipeline_layout_desc) + .map_err(DeviceError::from_hal)? + }; + + let supports_indirect_first_instance = + required_features.contains(wgt::Features::INDIRECT_FIRST_INSTANCE); + let pipeline = create_validation_pipeline( + device, + module.as_ref(), + pipeline_layout.as_ref(), + supports_indirect_first_instance, + )?; + + Ok(Self { + module, + metadata_bind_group_layout, + src_bind_group_layout, + dst_bind_group_layout, + pipeline_layout, + pipeline, + + free_indirect_entries: Mutex::new(rank::BUFFER_POOL, Vec::new()), + free_metadata_entries: Mutex::new(rank::BUFFER_POOL, Vec::new()), + }) + } + + /// `Ok(None)` will only be returned if `buffer_size` is `0`. + pub(super) fn create_src_bind_group( + &self, + device: &dyn hal::DynDevice, + limits: &Limits, + buffer_size: u64, + buffer: &dyn hal::DynBuffer, + ) -> Result>, DeviceError> { + let binding_size = calculate_src_buffer_binding_size(buffer_size, limits); + let Some(binding_size) = NonZeroU64::new(binding_size) else { + return Ok(None); + }; + let hal_desc = hal::BindGroupDescriptor { + label: None, + layout: self.src_bind_group_layout.as_ref(), + entries: &[hal::BindGroupEntry { + binding: 0, + resource_index: 0, + count: 1, + }], + buffers: &[hal::BufferBinding { + buffer, + offset: 0, + size: Some(binding_size), + }], + samplers: &[], + textures: &[], + acceleration_structures: &[], + }; + unsafe { + device + .create_bind_group(&hal_desc) + .map(Some) + .map_err(DeviceError::from_hal) + } + } + + fn acquire_dst_entry( + &self, + device: &dyn hal::DynDevice, + ) -> Result { + let mut free_buffers = self.free_indirect_entries.lock(); + match free_buffers.pop() { + Some(buffer) => Ok(buffer), + None => { + let usage = wgt::BufferUses::INDIRECT | wgt::BufferUses::STORAGE_READ_WRITE; + create_buffer_and_bind_group(device, usage, self.dst_bind_group_layout.as_ref()) + } + } + } + + fn release_dst_entries(&self, entries: impl Iterator) { + self.free_indirect_entries.lock().extend(entries); + } + + fn acquire_metadata_entry( + &self, + device: &dyn hal::DynDevice, + ) -> Result { + let mut free_buffers = self.free_metadata_entries.lock(); + match free_buffers.pop() { + Some(buffer) => Ok(buffer), + None => { + let usage = wgt::BufferUses::COPY_DST | wgt::BufferUses::STORAGE_READ_ONLY; + create_buffer_and_bind_group( + device, + usage, + self.metadata_bind_group_layout.as_ref(), + ) + } + } + } + + fn release_metadata_entries(&self, entries: impl Iterator) { + self.free_metadata_entries.lock().extend(entries); + } + + /// Injects a compute pass that will validate all indirect draws in the current render pass. + pub(crate) fn inject_validation_pass( + &self, + device: &Arc, + snatch_guard: &SnatchGuard, + resources: &mut DrawResources, + temp_resources: &mut Vec, + encoder: &mut dyn hal::DynCommandEncoder, + batcher: DrawBatcher, + ) -> Result<(), DeviceError> { + let mut batches = batcher.batches; + + if batches.is_empty() { + return Ok(()); + } + + let max_staging_buffer_size = 1 << 26; // ~67MiB + + let mut staging_buffers = Vec::new(); + + let mut current_size = 0; + for batch in batches.values_mut() { + let data = batch.metadata(); + let offset = if current_size + data.len() > max_staging_buffer_size { + let staging_buffer = + StagingBuffer::new(device, NonZeroU64::new(current_size as u64).unwrap())?; + staging_buffers.push(staging_buffer); + current_size = data.len(); + 0 + } else { + let offset = current_size; + current_size += data.len(); + offset as u64 + }; + batch.staging_buffer_index = staging_buffers.len(); + batch.staging_buffer_offset = offset; + } + if current_size != 0 { + let staging_buffer = + StagingBuffer::new(device, NonZeroU64::new(current_size as u64).unwrap())?; + staging_buffers.push(staging_buffer); + } + + for batch in batches.values() { + let data = batch.metadata(); + let staging_buffer = &mut staging_buffers[batch.staging_buffer_index]; + unsafe { + staging_buffer.write_with_offset( + data, + 0, + batch.staging_buffer_offset as isize, + data.len(), + ) + }; + } + + let staging_buffers: Vec<_> = staging_buffers + .into_iter() + .map(|buffer| buffer.flush()) + .collect(); + + let mut current_metadata_entry = None; + for batch in batches.values_mut() { + let data = batch.metadata(); + let (metadata_resource_index, metadata_buffer_offset) = + resources.get_metadata_subrange(data.len() as u64, &mut current_metadata_entry)?; + batch.metadata_resource_index = metadata_resource_index; + batch.metadata_buffer_offset = metadata_buffer_offset; + } + + let buffer_barrier_scratch = &mut BufferBarrierScratch::new(); + let unique_index_scratch = &mut UniqueIndexScratch::new(); + + BufferBarriers::new(buffer_barrier_scratch) + .extend( + batches + .values() + .map(|batch| batch.staging_buffer_index) + .unique(unique_index_scratch) + .map(|index| hal::BufferBarrier { + buffer: staging_buffers[index].raw(), + usage: hal::StateTransition { + from: wgt::BufferUses::MAP_WRITE, + to: wgt::BufferUses::COPY_SRC, + }, + }), + ) + .extend( + batches + .values() + .map(|batch| batch.metadata_resource_index) + .unique(unique_index_scratch) + .map(|index| hal::BufferBarrier { + buffer: resources.get_metadata_buffer(index), + usage: hal::StateTransition { + from: wgt::BufferUses::STORAGE_READ_ONLY, + to: wgt::BufferUses::COPY_DST, + }, + }), + ) + .encode(encoder); + + for batch in batches.values() { + let data = batch.metadata(); + let data_size = NonZeroU64::new(data.len() as u64).unwrap(); + + let staging_buffer = &staging_buffers[batch.staging_buffer_index]; + + let metadata_buffer = resources.get_metadata_buffer(batch.metadata_resource_index); + + unsafe { + encoder.copy_buffer_to_buffer( + staging_buffer.raw(), + metadata_buffer, + &[hal::BufferCopy { + src_offset: batch.staging_buffer_offset, + dst_offset: batch.metadata_buffer_offset, + size: data_size, + }], + ); + } + } + + for staging_buffer in staging_buffers { + temp_resources.push(TempResource::StagingBuffer(staging_buffer)); + } + + BufferBarriers::new(buffer_barrier_scratch) + .extend( + batches + .values() + .map(|batch| batch.metadata_resource_index) + .unique(unique_index_scratch) + .map(|index| hal::BufferBarrier { + buffer: resources.get_metadata_buffer(index), + usage: hal::StateTransition { + from: wgt::BufferUses::COPY_DST, + to: wgt::BufferUses::STORAGE_READ_ONLY, + }, + }), + ) + .extend( + batches + .values() + .map(|batch| batch.dst_resource_index) + .unique(unique_index_scratch) + .map(|index| hal::BufferBarrier { + buffer: resources.get_dst_buffer(index), + usage: hal::StateTransition { + from: wgt::BufferUses::INDIRECT, + to: wgt::BufferUses::STORAGE_READ_WRITE, + }, + }), + ) + .encode(encoder); + + let desc = hal::ComputePassDescriptor { + label: None, + timestamp_writes: None, + }; + unsafe { + encoder.begin_compute_pass(&desc); + } + unsafe { + encoder.set_compute_pipeline(self.pipeline.as_ref()); + } + + for batch in batches.values() { + let pipeline_layout = self.pipeline_layout.as_ref(); + + let metadata_start = + (batch.metadata_buffer_offset / size_of::() as u64) as u32; + let metadata_count = batch.entries.len() as u32; + unsafe { + encoder.set_push_constants( + pipeline_layout, + wgt::ShaderStages::COMPUTE, + 0, + &[metadata_start, metadata_count], + ); + } + + let metadata_bind_group = + resources.get_metadata_bind_group(batch.metadata_resource_index); + unsafe { + encoder.set_bind_group(pipeline_layout, 0, Some(metadata_bind_group), &[]); + } + + let src_bind_group = batch + .src_buffer + .indirect_validation_bind_groups + .get(snatch_guard) + .unwrap() + .draw + .as_ref(); + unsafe { + encoder.set_bind_group( + pipeline_layout, + 1, + Some(src_bind_group), + &[batch.src_dynamic_offset as u32], + ); + } + + let dst_bind_group = resources.get_dst_bind_group(batch.dst_resource_index); + unsafe { + encoder.set_bind_group(pipeline_layout, 2, Some(dst_bind_group), &[]); + } + + unsafe { + encoder.dispatch([(batch.entries.len() as u32).div_ceil(64), 1, 1]); + } + } + + unsafe { + encoder.end_compute_pass(); + } + + BufferBarriers::new(buffer_barrier_scratch) + .extend( + batches + .values() + .map(|batch| batch.dst_resource_index) + .unique(unique_index_scratch) + .map(|index| hal::BufferBarrier { + buffer: resources.get_dst_buffer(index), + usage: hal::StateTransition { + from: wgt::BufferUses::STORAGE_READ_WRITE, + to: wgt::BufferUses::INDIRECT, + }, + }), + ) + .encode(encoder); + + Ok(()) + } + + pub(super) fn dispose(self, device: &dyn hal::DynDevice) { + let Draw { + module, + metadata_bind_group_layout, + src_bind_group_layout, + dst_bind_group_layout, + pipeline_layout, + pipeline, + + free_indirect_entries, + free_metadata_entries, + } = self; + + for entry in free_indirect_entries.into_inner().drain(..) { + unsafe { + device.destroy_bind_group(entry.bind_group); + device.destroy_buffer(entry.buffer); + } + } + + for entry in free_metadata_entries.into_inner().drain(..) { + unsafe { + device.destroy_bind_group(entry.bind_group); + device.destroy_buffer(entry.buffer); + } + } + + unsafe { + device.destroy_compute_pipeline(pipeline); + device.destroy_pipeline_layout(pipeline_layout); + device.destroy_bind_group_layout(metadata_bind_group_layout); + device.destroy_bind_group_layout(src_bind_group_layout); + device.destroy_bind_group_layout(dst_bind_group_layout); + device.destroy_shader_module(module); + } + } +} + +fn create_validation_module( + device: &dyn hal::DynDevice, +) -> Result, CreateIndirectValidationPipelineError> { + let src = include_str!("./validate_draw.wgsl"); + + #[cfg(feature = "wgsl")] + let module = naga::front::wgsl::parse_str(src).map_err(|inner| { + CreateShaderModuleError::Parsing(naga::error::ShaderError { + source: src.to_string(), + label: None, + inner: Box::new(inner), + }) + })?; + #[cfg(not(feature = "wgsl"))] + #[allow(clippy::diverging_sub_expression)] + let module = panic!("Indirect validation requires the wgsl feature flag to be enabled!"); + + let info = crate::device::create_validator( + wgt::Features::PUSH_CONSTANTS, + wgt::DownlevelFlags::empty(), + naga::valid::ValidationFlags::all(), + ) + .validate(&module) + .map_err(|inner| { + CreateShaderModuleError::Validation(naga::error::ShaderError { + source: src.to_string(), + label: None, + inner: Box::new(inner), + }) + })?; + let hal_shader = hal::ShaderInput::Naga(hal::NagaShader { + module: alloc::borrow::Cow::Owned(module), + info, + debug_source: None, + }); + let hal_desc = hal::ShaderModuleDescriptor { + label: None, + runtime_checks: wgt::ShaderRuntimeChecks::unchecked(), + }; + let module = unsafe { device.create_shader_module(&hal_desc, hal_shader) }.map_err( + |error| match error { + hal::ShaderError::Device(error) => { + CreateShaderModuleError::Device(DeviceError::from_hal(error)) + } + hal::ShaderError::Compilation(ref msg) => { + log::error!("Shader error: {}", msg); + CreateShaderModuleError::Generation + } + }, + )?; + + Ok(module) +} + +fn create_validation_pipeline( + device: &dyn hal::DynDevice, + module: &dyn hal::DynShaderModule, + pipeline_layout: &dyn hal::DynPipelineLayout, + supports_indirect_first_instance: bool, +) -> Result, CreateIndirectValidationPipelineError> { + let pipeline_desc = hal::ComputePipelineDescriptor { + label: None, + layout: pipeline_layout, + stage: hal::ProgrammableStage { + module, + entry_point: "main", + constants: &hashbrown::HashMap::from([( + "supports_indirect_first_instance".to_string(), + f64::from(supports_indirect_first_instance), + )]), + zero_initialize_workgroup_memory: false, + }, + cache: None, + }; + let pipeline = + unsafe { device.create_compute_pipeline(&pipeline_desc) }.map_err(|err| match err { + hal::PipelineError::Device(error) => { + CreateComputePipelineError::Device(DeviceError::from_hal(error)) + } + hal::PipelineError::Linkage(_stages, msg) => CreateComputePipelineError::Internal(msg), + hal::PipelineError::EntryPoint(_stage) => CreateComputePipelineError::Internal( + crate::device::ENTRYPOINT_FAILURE_ERROR.to_string(), + ), + hal::PipelineError::PipelineConstants(_, error) => { + CreateComputePipelineError::PipelineConstants(error) + } + })?; + + Ok(pipeline) +} + +fn create_bind_group_layout( + device: &dyn hal::DynDevice, + read_only: bool, + has_dynamic_offset: bool, + min_binding_size: wgt::BufferSize, +) -> Result, CreateIndirectValidationPipelineError> { + let bind_group_layout_desc = hal::BindGroupLayoutDescriptor { + label: None, + flags: hal::BindGroupLayoutFlags::empty(), + entries: &[wgt::BindGroupLayoutEntry { + binding: 0, + visibility: wgt::ShaderStages::COMPUTE, + ty: wgt::BindingType::Buffer { + ty: wgt::BufferBindingType::Storage { read_only }, + has_dynamic_offset, + min_binding_size: Some(min_binding_size), + }, + count: None, + }], + }; + let bind_group_layout = unsafe { + device + .create_bind_group_layout(&bind_group_layout_desc) + .map_err(DeviceError::from_hal)? + }; + + Ok(bind_group_layout) +} + +/// Returns the largest binding size that when combined with dynamic offsets can address the whole buffer. +fn calculate_src_buffer_binding_size(buffer_size: u64, limits: &Limits) -> u64 { + let max_storage_buffer_binding_size = limits.max_storage_buffer_binding_size as u64; + let min_storage_buffer_offset_alignment = limits.min_storage_buffer_offset_alignment as u64; + + if buffer_size <= max_storage_buffer_binding_size { + buffer_size + } else { + let buffer_rem = buffer_size % min_storage_buffer_offset_alignment; + let binding_rem = max_storage_buffer_binding_size % min_storage_buffer_offset_alignment; + + // Can the buffer remainder fit in the binding remainder? + // If so, align max binding size and add buffer remainder + if buffer_rem <= binding_rem { + max_storage_buffer_binding_size - binding_rem + buffer_rem + } + // If not, align max binding size, shorten it by a chunk and add buffer remainder + else { + max_storage_buffer_binding_size - binding_rem - min_storage_buffer_offset_alignment + + buffer_rem + } + } +} + +/// Splits the given `offset` into a dynamic offset & offset. +fn calculate_src_offsets(buffer_size: u64, limits: &Limits, offset: u64) -> (u64, u64) { + let binding_size = calculate_src_buffer_binding_size(buffer_size, limits); + + let min_storage_buffer_offset_alignment = limits.min_storage_buffer_offset_alignment as u64; + + let chunk_adjustment = match min_storage_buffer_offset_alignment { + // No need to adjust since the src_offset is 4 byte aligned. + 4 => 0, + // With 16/20 bytes of data we can straddle up to 2 8 byte boundaries: + // - 16 bytes of data: (4|8|4) + // - 20 bytes of data: (4|8|8, 8|8|4) + 8 => 2, + // With 16/20 bytes of data we can straddle up to 1 16+ byte boundary: + // - 16 bytes of data: (4|12, 8|8, 12|4) + // - 20 bytes of data: (4|16, 8|12, 12|8, 16|4) + 16.. => 1, + _ => unreachable!(), + }; + + let chunks = binding_size / min_storage_buffer_offset_alignment; + let dynamic_offset_stride = + chunks.saturating_sub(chunk_adjustment) * min_storage_buffer_offset_alignment; + + if dynamic_offset_stride == 0 { + return (0, offset); + } + + let max_dynamic_offset = buffer_size - binding_size; + let max_dynamic_offset_index = max_dynamic_offset / dynamic_offset_stride; + + let src_dynamic_offset_index = offset / dynamic_offset_stride; + + let src_dynamic_offset = + src_dynamic_offset_index.min(max_dynamic_offset_index) * dynamic_offset_stride; + let src_offset = offset - src_dynamic_offset; + + (src_dynamic_offset, src_offset) +} + +#[derive(Debug)] +struct BufferPoolEntry { + buffer: Box, + bind_group: Box, +} + +fn create_buffer_and_bind_group( + device: &dyn hal::DynDevice, + usage: wgt::BufferUses, + bind_group_layout: &dyn hal::DynBindGroupLayout, +) -> Result { + let buffer_desc = hal::BufferDescriptor { + label: None, + size: BUFFER_SIZE.get(), + usage, + memory_flags: hal::MemoryFlags::empty(), + }; + let buffer = unsafe { device.create_buffer(&buffer_desc) }?; + let bind_group_desc = hal::BindGroupDescriptor { + label: None, + layout: bind_group_layout, + entries: &[hal::BindGroupEntry { + binding: 0, + resource_index: 0, + count: 1, + }], + buffers: &[hal::BufferBinding { + buffer: buffer.as_ref(), + offset: 0, + size: Some(BUFFER_SIZE), + }], + samplers: &[], + textures: &[], + acceleration_structures: &[], + }; + let bind_group = unsafe { device.create_bind_group(&bind_group_desc) }?; + Ok(BufferPoolEntry { buffer, bind_group }) +} + +#[derive(Clone)] +struct CurrentEntry { + index: usize, + offset: u64, +} + +/// Holds all command buffer-level resources that are needed to validate indirect draws. +pub(crate) struct DrawResources { + device: Arc, + dst_entries: Vec, + metadata_entries: Vec, +} + +impl Drop for DrawResources { + fn drop(&mut self) { + if let Some(ref indirect_validation) = self.device.indirect_validation { + let indirect_draw_validation = &indirect_validation.draw; + indirect_draw_validation.release_dst_entries(self.dst_entries.drain(..)); + indirect_draw_validation.release_metadata_entries(self.metadata_entries.drain(..)); + } + } +} + +impl DrawResources { + pub(crate) fn new(device: Arc) -> Self { + DrawResources { + device, + dst_entries: Vec::new(), + metadata_entries: Vec::new(), + } + } + + pub(crate) fn get_dst_buffer(&self, index: usize) -> &dyn hal::DynBuffer { + self.dst_entries.get(index).unwrap().buffer.as_ref() + } + + fn get_dst_bind_group(&self, index: usize) -> &dyn hal::DynBindGroup { + self.dst_entries.get(index).unwrap().bind_group.as_ref() + } + + fn get_metadata_buffer(&self, index: usize) -> &dyn hal::DynBuffer { + self.metadata_entries.get(index).unwrap().buffer.as_ref() + } + + fn get_metadata_bind_group(&self, index: usize) -> &dyn hal::DynBindGroup { + self.metadata_entries + .get(index) + .unwrap() + .bind_group + .as_ref() + } + + fn get_dst_subrange( + &mut self, + size: u64, + current_entry: &mut Option, + ) -> Result<(usize, u64), DeviceError> { + let indirect_draw_validation = &self.device.indirect_validation.as_ref().unwrap().draw; + let ensure_entry = |index: usize| { + if self.dst_entries.len() <= index { + let entry = indirect_draw_validation.acquire_dst_entry(self.device.raw())?; + self.dst_entries.push(entry); + } + Ok(()) + }; + let entry_data = Self::get_subrange_impl(ensure_entry, current_entry, size)?; + Ok((entry_data.index, entry_data.offset)) + } + + fn get_metadata_subrange( + &mut self, + size: u64, + current_entry: &mut Option, + ) -> Result<(usize, u64), DeviceError> { + let indirect_draw_validation = &self.device.indirect_validation.as_ref().unwrap().draw; + let ensure_entry = |index: usize| { + if self.metadata_entries.len() <= index { + let entry = indirect_draw_validation.acquire_metadata_entry(self.device.raw())?; + self.metadata_entries.push(entry); + } + Ok(()) + }; + let entry_data = Self::get_subrange_impl(ensure_entry, current_entry, size)?; + Ok((entry_data.index, entry_data.offset)) + } + + fn get_subrange_impl( + ensure_entry: impl FnOnce(usize) -> Result<(), hal::DeviceError>, + current_entry: &mut Option, + size: u64, + ) -> Result { + let index = if let Some(current_entry) = current_entry.as_mut() { + if current_entry.offset + size <= BUFFER_SIZE.get() { + let entry_data = current_entry.clone(); + current_entry.offset += size; + return Ok(entry_data); + } else { + current_entry.index + 1 + } + } else { + 0 + }; + + ensure_entry(index).map_err(DeviceError::from_hal)?; + + let entry_data = CurrentEntry { index, offset: 0 }; + + *current_entry = Some(CurrentEntry { + index, + offset: size, + }); + + Ok(entry_data) + } +} + +/// This must match the `MetadataEntry` struct used by the shader. +#[repr(C)] +struct MetadataEntry { + src_offset: u32, + dst_offset: u32, + vertex_or_index_limit: u32, + instance_limit: u32, +} + +impl MetadataEntry { + fn new( + indexed: bool, + src_offset: u64, + dst_offset: u64, + vertex_or_index_limit: u64, + instance_limit: u64, + ) -> Self { + debug_assert_eq!( + 4, + size_of_val(&Limits::default().max_storage_buffer_binding_size) + ); + + let src_offset = src_offset as u32; // max_storage_buffer_binding_size is a u32 + let src_offset = src_offset / 4; // translate byte offset to offset in u32's + + // `src_offset` needs at most 30 bits, + // pack `indexed` in bit 31 of `src_offset` + let src_offset = src_offset | ((indexed as u32) << 31); + + // max value for limits since first_X and X_count indirect draw arguments are u32 + let max_limit = u32::MAX as u64 + u32::MAX as u64; // 1 11111111 11111111 11111111 11111110 + + let vertex_or_index_limit = vertex_or_index_limit.min(max_limit); + let vertex_or_index_limit_bit_32 = (vertex_or_index_limit >> 32) as u32; // extract bit 32 + let vertex_or_index_limit = vertex_or_index_limit as u32; // truncate the limit to a u32 + + let instance_limit = instance_limit.min(max_limit); + let instance_limit_bit_32 = (instance_limit >> 32) as u32; // extract bit 32 + let instance_limit = instance_limit as u32; // truncate the limit to a u32 + + let dst_offset = dst_offset as u32; // max_storage_buffer_binding_size is a u32 + let dst_offset = dst_offset / 4; // translate byte offset to offset in u32's + + // `dst_offset` needs at most 30 bits, + // pack `vertex_or_index_limit_bit_32` in bit 30 of `dst_offset` and + // pack `instance_limit_bit_32` in bit 31 of `dst_offset` + let dst_offset = + dst_offset | (vertex_or_index_limit_bit_32 << 30) | (instance_limit_bit_32 << 31); + + Self { + src_offset, + dst_offset, + vertex_or_index_limit, + instance_limit, + } + } +} + +struct DrawIndirectValidationBatch { + src_buffer: Arc, + src_dynamic_offset: u64, + dst_resource_index: usize, + entries: Vec, + + staging_buffer_index: usize, + staging_buffer_offset: u64, + metadata_resource_index: usize, + metadata_buffer_offset: u64, +} + +impl DrawIndirectValidationBatch { + /// Data to be written to the metadata buffer. + fn metadata(&self) -> &[u8] { + unsafe { + core::slice::from_raw_parts( + self.entries.as_ptr().cast::(), + self.entries.len() * size_of::(), + ) + } + } +} + +/// Accumulates all needed data needed to validate indirect draws. +pub(crate) struct DrawBatcher { + batches: FastHashMap<(TrackerIndex, u64, usize), DrawIndirectValidationBatch>, + current_dst_entry: Option, +} + +impl DrawBatcher { + pub(crate) fn new() -> Self { + Self { + batches: FastHashMap::default(), + current_dst_entry: None, + } + } + + /// Add an indirect draw to be validated. + /// + /// Returns the index of the indirect buffer in `indirect_draw_validation_resources` + /// and the offset to be used for the draw. + pub(crate) fn add<'a>( + &mut self, + indirect_draw_validation_resources: &'a mut DrawResources, + device: &Device, + src_buffer: &Arc, + offset: u64, + indexed: bool, + vertex_or_index_limit: u64, + instance_limit: u64, + ) -> Result<(usize, u64), DeviceError> { + let stride = crate::command::get_stride_of_indirect_args(indexed); + + let (dst_resource_index, dst_offset) = indirect_draw_validation_resources + .get_dst_subrange(stride, &mut self.current_dst_entry)?; + + let buffer_size = src_buffer.size; + let limits = device.adapter.limits(); + let (src_dynamic_offset, src_offset) = calculate_src_offsets(buffer_size, &limits, offset); + + let src_buffer_tracker_index = src_buffer.tracker_index(); + + let entry = MetadataEntry::new( + indexed, + src_offset, + dst_offset, + vertex_or_index_limit, + instance_limit, + ); + + match self.batches.entry(( + src_buffer_tracker_index, + src_dynamic_offset, + dst_resource_index, + )) { + hashbrown::hash_map::Entry::Occupied(mut occupied_entry) => { + occupied_entry.get_mut().entries.push(entry) + } + hashbrown::hash_map::Entry::Vacant(vacant_entry) => { + vacant_entry.insert(DrawIndirectValidationBatch { + src_buffer: src_buffer.clone(), + src_dynamic_offset, + dst_resource_index, + entries: vec![entry], + + // these will be initialized once we accumulated all entries for the batch + staging_buffer_index: 0, + staging_buffer_offset: 0, + metadata_resource_index: 0, + metadata_buffer_offset: 0, + }); + } + } + + Ok((dst_resource_index, dst_offset)) + } +} diff --git a/third_party/rust/wgpu-core/src/indirect_validation/mod.rs b/third_party/rust/wgpu-core/src/indirect_validation/mod.rs new file mode 100644 index 000000000000..c4977aa90d6c --- /dev/null +++ b/third_party/rust/wgpu-core/src/indirect_validation/mod.rs @@ -0,0 +1,105 @@ +use crate::{ + device::DeviceError, + pipeline::{CreateComputePipelineError, CreateShaderModuleError}, +}; +use alloc::boxed::Box; +use thiserror::Error; + +mod dispatch; +mod draw; +mod utils; + +pub(crate) use dispatch::Dispatch; +pub(crate) use draw::{Draw, DrawBatcher, DrawResources}; + +#[derive(Clone, Debug, Error)] +#[non_exhaustive] +enum CreateIndirectValidationPipelineError { + #[error(transparent)] + DeviceError(#[from] DeviceError), + #[error(transparent)] + ShaderModule(#[from] CreateShaderModuleError), + #[error(transparent)] + ComputePipeline(#[from] CreateComputePipelineError), +} + +pub(crate) struct IndirectValidation { + pub(crate) dispatch: Dispatch, + pub(crate) draw: Draw, +} + +impl IndirectValidation { + pub(crate) fn new( + device: &dyn hal::DynDevice, + required_limits: &wgt::Limits, + required_features: &wgt::Features, + ) -> Result { + let dispatch = match Dispatch::new(device, required_limits) { + Ok(dispatch) => dispatch, + Err(e) => { + log::error!("indirect-validation error: {e:?}"); + return Err(DeviceError::Lost); + } + }; + let draw = match Draw::new(device, required_features) { + Ok(draw) => draw, + Err(e) => { + log::error!("indirect-draw-validation error: {e:?}"); + return Err(DeviceError::Lost); + } + }; + Ok(Self { dispatch, draw }) + } + + pub(crate) fn dispose(self, device: &dyn hal::DynDevice) { + let Self { dispatch, draw } = self; + + dispatch.dispose(device); + draw.dispose(device); + } +} + +#[derive(Debug)] +pub(crate) struct BindGroups { + pub(crate) dispatch: Box, + draw: Box, +} + +impl BindGroups { + /// `Ok(None)` will only be returned if `buffer_size` is `0`. + pub(crate) fn new( + indirect_validation: &IndirectValidation, + device: &crate::device::Device, + buffer_size: u64, + buffer: &dyn hal::DynBuffer, + ) -> Result, DeviceError> { + let dispatch = indirect_validation.dispatch.create_src_bind_group( + device.raw(), + &device.limits, + buffer_size, + buffer, + )?; + let draw = indirect_validation.draw.create_src_bind_group( + device.raw(), + &device.adapter.limits(), + buffer_size, + buffer, + )?; + + match (dispatch, draw) { + (None, None) => Ok(None), + (None, Some(_)) => unreachable!(), + (Some(_), None) => unreachable!(), + (Some(dispatch), Some(draw)) => Ok(Some(Self { dispatch, draw })), + } + } + + pub(crate) fn dispose(self, device: &dyn hal::DynDevice) { + let Self { dispatch, draw } = self; + + unsafe { + device.destroy_bind_group(dispatch); + device.destroy_bind_group(draw); + } + } +} diff --git a/third_party/rust/wgpu-core/src/indirect_validation/utils.rs b/third_party/rust/wgpu-core/src/indirect_validation/utils.rs new file mode 100644 index 000000000000..4c638d048557 --- /dev/null +++ b/third_party/rust/wgpu-core/src/indirect_validation/utils.rs @@ -0,0 +1,84 @@ +use alloc::vec::Vec; + +pub(crate) struct UniqueIndexScratch(bit_set::BitSet); + +impl UniqueIndexScratch { + pub(crate) fn new() -> Self { + Self(bit_set::BitSet::new()) + } +} + +pub(crate) struct UniqueIndex<'a, I: Iterator> { + inner: I, + scratch: &'a mut UniqueIndexScratch, +} + +impl<'a, I: Iterator> UniqueIndex<'a, I> { + fn new(inner: I, scratch: &'a mut UniqueIndexScratch) -> Self { + scratch.0.clear(); + Self { inner, scratch } + } +} + +impl<'a, I: Iterator> Iterator for UniqueIndex<'a, I> { + type Item = usize; + + fn next(&mut self) -> Option { + self.inner.find(|&i| self.scratch.0.insert(i)) + } +} + +pub(crate) trait UniqueIndexExt: Iterator { + fn unique<'a>(self, scratch: &'a mut UniqueIndexScratch) -> UniqueIndex<'a, Self> + where + Self: Sized, + { + UniqueIndex::new(self, scratch) + } +} + +impl> UniqueIndexExt for T {} + +type BufferBarrier<'b> = hal::BufferBarrier<'b, dyn hal::DynBuffer>; + +pub(crate) struct BufferBarrierScratch<'b>(Vec>); + +impl<'b> BufferBarrierScratch<'b> { + pub(crate) fn new() -> Self { + Self(Vec::new()) + } +} + +pub(crate) struct BufferBarriers<'a, 'b> { + scratch: &'a mut BufferBarrierScratch<'b>, +} + +impl<'a, 'b> BufferBarriers<'a, 'b> { + pub(crate) fn new(scratch: &'a mut BufferBarrierScratch<'_>) -> Self { + // change lifetime of buffer reference, this is safe since `scratch` is empty, + // it was either just created or it has been cleared on `BufferBarriers::drop` + let scratch = unsafe { + core::mem::transmute::<&'a mut BufferBarrierScratch<'_>, &'a mut BufferBarrierScratch<'b>>( + scratch, + ) + }; + Self { scratch } + } + + pub(crate) fn extend(self, iter: impl Iterator>) -> Self { + self.scratch.0.extend(iter); + self + } + + pub(crate) fn encode(self, encoder: &mut dyn hal::DynCommandEncoder) { + unsafe { + encoder.transition_buffers(&self.scratch.0); + } + } +} + +impl<'a, 'b> Drop for BufferBarriers<'a, 'b> { + fn drop(&mut self) { + self.scratch.0.clear(); + } +} diff --git a/third_party/rust/wgpu-core/src/indirect_validation/validate_draw.wgsl b/third_party/rust/wgpu-core/src/indirect_validation/validate_draw.wgsl new file mode 100644 index 000000000000..53a9bb8d7289 --- /dev/null +++ b/third_party/rust/wgpu-core/src/indirect_validation/validate_draw.wgsl @@ -0,0 +1,86 @@ +override supports_indirect_first_instance: bool; + +struct MetadataEntry { + // bits 0..30 are an offset into `src` + // bit 31 signifies that we are validating an indexed draw + src_offset: u32, + // bits 0..30 are an offset into `dst` + // bit 30 is the most significant bit of `vertex_or_index_limit` + // bit 31 is the most significant bit of `instance_limit` + dst_offset: u32, + vertex_or_index_limit: u32, + instance_limit: u32, +} + +struct MetadataRange { + start: u32, + count: u32, +} +var metadata_range: MetadataRange; + +@group(0) @binding(0) +var metadata: array; +@group(1) @binding(0) +var src: array; +@group(2) @binding(0) +var dst: array; + +fn is_bit_set(data: u32, index: u32) -> bool { + return ((data >> index) & 1u) == 1u; +} + +@compute @workgroup_size(64) +fn main(@builtin(global_invocation_id) global_invocation_id: vec3u) { + if global_invocation_id.x >= metadata_range.count { return; } + + let metadata = metadata[metadata_range.start + global_invocation_id.x]; + var failed = false; + + let is_indexed = is_bit_set(metadata.src_offset, 31); + let src_base_offset = ((metadata.src_offset << 2) >> 2); + let dst_base_offset = ((metadata.dst_offset << 2) >> 2); + + let first_vertex_or_index = src[src_base_offset + 2]; + let vertex_or_index_count = src[src_base_offset + 0]; + + { + let can_overflow = is_bit_set(metadata.dst_offset, 30); + let sub_overflows = metadata.vertex_or_index_limit < first_vertex_or_index; + failed |= sub_overflows && !can_overflow; + let vertex_or_index_limit = metadata.vertex_or_index_limit - first_vertex_or_index; + failed |= vertex_or_index_limit < vertex_or_index_count; + } + + let first_instance = src[src_base_offset + 3 + u32(is_indexed)]; + let instance_count = src[src_base_offset + 1]; + + { + let can_overflow = is_bit_set(metadata.dst_offset, 31); + let sub_overflows = metadata.instance_limit < first_instance; + failed |= sub_overflows && !can_overflow; + let instance_limit = metadata.instance_limit - first_instance; + failed |= instance_limit < instance_count; + } + + if !supports_indirect_first_instance { + failed |= first_instance != 0u; + } + + if failed { + dst[dst_base_offset + 0] = 0u; + dst[dst_base_offset + 1] = 0u; + dst[dst_base_offset + 2] = 0u; + dst[dst_base_offset + 3] = 0u; + if (is_indexed) { + dst[dst_base_offset + 4] = 0u; + } + } else { + dst[dst_base_offset + 0] = src[src_base_offset + 0]; + dst[dst_base_offset + 1] = src[src_base_offset + 1]; + dst[dst_base_offset + 2] = src[src_base_offset + 2]; + dst[dst_base_offset + 3] = src[src_base_offset + 3]; + if (is_indexed) { + dst[dst_base_offset + 4] = src[src_base_offset + 4]; + } + } +} \ No newline at end of file diff --git a/third_party/rust/wgpu-core/src/init_tracker/texture.rs b/third_party/rust/wgpu-core/src/init_tracker/texture.rs index af9856b3093e..e03f8d3a0e40 100644 --- a/third_party/rust/wgpu-core/src/init_tracker/texture.rs +++ b/third_party/rust/wgpu-core/src/init_tracker/texture.rs @@ -53,9 +53,11 @@ pub(crate) struct TextureInitTracker { impl TextureInitTracker { pub(crate) fn new(mip_level_count: u32, depth_or_array_layers: u32) -> Self { TextureInitTracker { - mips: core::iter::repeat(TextureLayerInitTracker::new(depth_or_array_layers)) - .take(mip_level_count as usize) - .collect(), + mips: core::iter::repeat_n( + TextureLayerInitTracker::new(depth_or_array_layers), + mip_level_count as usize, + ) + .collect(), } } diff --git a/third_party/rust/wgpu-core/src/lib.rs b/third_party/rust/wgpu-core/src/lib.rs index cdae6b927afd..94a22e796967 100644 --- a/third_party/rust/wgpu-core/src/lib.rs +++ b/third_party/rust/wgpu-core/src/lib.rs @@ -33,9 +33,6 @@ clippy::needless_update, // Need many arguments for some core functions to be able to re-use code in many situations. clippy::too_many_arguments, - // For some reason `rustc` can warn about these in const generics even - // though they are required. - unused_braces, // It gets in the way a lot and does not prevent bugs in practice. clippy::pattern_type_mismatch, // `wgpu-core` isn't entirely user-facing, so it's useful to document internal items. @@ -79,7 +76,6 @@ mod hash_utils; pub mod hub; pub mod id; pub mod identity; -#[cfg(feature = "indirect-validation")] mod indirect_validation; mod init_tracker; pub mod instance; diff --git a/third_party/rust/wgpu-core/src/lock/observing.rs b/third_party/rust/wgpu-core/src/lock/observing.rs index 3c880ca02f5e..b50477dab53d 100644 --- a/third_party/rust/wgpu-core/src/lock/observing.rs +++ b/third_party/rust/wgpu-core/src/lock/observing.rs @@ -36,7 +36,7 @@ use std::{ fs::File, panic::Location, path::{Path, PathBuf}, - vec::Vec, + string::String, }; use super::rank::{LockRank, LockRankSet}; @@ -80,6 +80,10 @@ impl Mutex { _state: LockStateGuard { saved }, } } + + pub fn into_inner(self) -> T { + self.inner.into_inner() + } } impl<'a, T> std::ops::Deref for MutexGuard<'a, T> { @@ -337,7 +341,7 @@ struct ObservationLog { locations_seen: FastHashSet<*const Location<'static>>, /// Buffer for serializing events, retained for allocation reuse. - buffer: Vec, + buffer: String, } #[allow(trivial_casts)] @@ -354,7 +358,7 @@ impl ObservationLog { Ok(ObservationLog { log_file, locations_seen: FastHashSet::default(), - buffer: Vec::new(), + buffer: String::new(), }) } @@ -404,9 +408,9 @@ impl ObservationLog { self.buffer.clear(); ron::ser::to_writer(&mut self.buffer, &action) .expect("error serializing `lock::observing::Action`"); - self.buffer.push(b'\n'); + self.buffer.push('\n'); self.log_file - .write_all(&self.buffer) + .write_all(self.buffer.as_bytes()) .expect("error writing `lock::observing::Action`"); } } diff --git a/third_party/rust/wgpu-core/src/lock/rank.rs b/third_party/rust/wgpu-core/src/lock/rank.rs index 652165ebda5f..35e46957fbaa 100644 --- a/third_party/rust/wgpu-core/src/lock/rank.rs +++ b/third_party/rust/wgpu-core/src/lock/rank.rs @@ -148,6 +148,7 @@ define_lock_ranks! { rank BLAS_BUILT_INDEX "Blas::built_index" followed by { } rank TLAS_BUILT_INDEX "Tlas::built_index" followed by { } rank TLAS_DEPENDENCIES "Tlas::dependencies" followed by { } + rank BUFFER_POOL "BufferPool::buffers" followed by { } #[cfg(test)] rank PAWN "pawn" followed by { ROOK, BISHOP } diff --git a/third_party/rust/wgpu-core/src/lock/vanilla.rs b/third_party/rust/wgpu-core/src/lock/vanilla.rs index 6318dae2d452..c085d0f9532d 100644 --- a/third_party/rust/wgpu-core/src/lock/vanilla.rs +++ b/third_party/rust/wgpu-core/src/lock/vanilla.rs @@ -30,6 +30,10 @@ impl Mutex { pub fn lock(&self) -> MutexGuard { MutexGuard(self.0.lock()) } + + pub fn into_inner(self) -> T { + self.0.into_inner() + } } impl<'a, T> ops::Deref for MutexGuard<'a, T> { diff --git a/third_party/rust/wgpu-core/src/pipeline.rs b/third_party/rust/wgpu-core/src/pipeline.rs index ebf3cdcae263..44171c37044f 100644 --- a/third_party/rust/wgpu-core/src/pipeline.rs +++ b/third_party/rust/wgpu-core/src/pipeline.rs @@ -101,7 +101,7 @@ impl ShaderModule { #[derive(Clone, Debug, Error)] #[non_exhaustive] pub enum CreateShaderModuleError { - #[cfg(any(feature = "wgsl", feature = "indirect-validation"))] + #[cfg(feature = "wgsl")] #[error(transparent)] Parsing(#[from] ShaderError), #[cfg(feature = "glsl")] diff --git a/third_party/rust/wgpu-core/src/pipeline_cache.rs b/third_party/rust/wgpu-core/src/pipeline_cache.rs index 0253163f6daf..c6737d88f4b8 100644 --- a/third_party/rust/wgpu-core/src/pipeline_cache.rs +++ b/third_party/rust/wgpu-core/src/pipeline_cache.rs @@ -483,7 +483,7 @@ mod tests { let cache = cache .into_iter() .flatten() - .chain(core::iter::repeat(0u8).take(100)) + .chain(core::iter::repeat_n(0u8, 100)) .collect::>(); let validation_result = super::validate_pipeline_cache(&cache, &ADAPTER, VALIDATION_KEY); let expected: &[u8] = &[0; 100]; @@ -504,7 +504,7 @@ mod tests { let cache = cache .into_iter() .flatten() - .chain(core::iter::repeat(0u8).take(200)) + .chain(core::iter::repeat_n(0u8, 200)) .collect::>(); let validation_result = super::validate_pipeline_cache(&cache, &ADAPTER, VALIDATION_KEY); assert_eq!(validation_result, Err(E::Extended)); diff --git a/third_party/rust/wgpu-core/src/resource.rs b/third_party/rust/wgpu-core/src/resource.rs index f7f0fd652758..f0fdb776e9f4 100644 --- a/third_party/rust/wgpu-core/src/resource.rs +++ b/third_party/rust/wgpu-core/src/resource.rs @@ -366,17 +366,13 @@ pub struct Buffer { pub(crate) tracking_data: TrackingData, pub(crate) map_state: Mutex, pub(crate) bind_groups: Mutex>, - #[cfg(feature = "indirect-validation")] - pub(crate) raw_indirect_validation_bind_group: Snatchable>, + pub(crate) indirect_validation_bind_groups: Snatchable, } impl Drop for Buffer { fn drop(&mut self) { - #[cfg(feature = "indirect-validation")] - if let Some(raw) = self.raw_indirect_validation_bind_group.take() { - unsafe { - self.device.raw().destroy_bind_group(raw); - } + if let Some(raw) = self.indirect_validation_bind_groups.take() { + raw.dispose(self.device.raw()); } if let Some(raw) = self.raw.take() { resource_log!("Destroy raw {}", self.error_ident()); @@ -439,10 +435,8 @@ impl Buffer { ) -> Result { let range_size = if let Some(size) = size { size - } else if offset > self.size { - 0 } else { - self.size - offset + self.size.saturating_sub(offset) }; if offset % wgt::MAP_ALIGNMENT != 0 { @@ -714,9 +708,8 @@ impl Buffer { } }; - #[cfg(feature = "indirect-validation")] - let raw_indirect_validation_bind_group = self - .raw_indirect_validation_bind_group + let indirect_validation_bind_groups = self + .indirect_validation_bind_groups .snatch(&mut snatch_guard); drop(snatch_guard); @@ -731,8 +724,7 @@ impl Buffer { device: Arc::clone(&self.device), label: self.label().to_owned(), bind_groups, - #[cfg(feature = "indirect-validation")] - raw_indirect_validation_bind_group, + indirect_validation_bind_groups, }) }; @@ -787,8 +779,7 @@ pub struct DestroyedBuffer { device: Arc, label: String, bind_groups: WeakVec, - #[cfg(feature = "indirect-validation")] - raw_indirect_validation_bind_group: Option>, + indirect_validation_bind_groups: Option, } impl DestroyedBuffer { @@ -805,11 +796,8 @@ impl Drop for DestroyedBuffer { ))); drop(deferred); - #[cfg(feature = "indirect-validation")] - if let Some(raw) = self.raw_indirect_validation_bind_group.take() { - unsafe { - self.device.raw().destroy_bind_group(raw); - } + if let Some(raw) = self.indirect_validation_bind_groups.take() { + raw.dispose(self.device.raw()); } resource_log!("Destroy raw Buffer (destroyed) {:?}", self.label()); diff --git a/third_party/rust/wgpu-core/src/validation.rs b/third_party/rust/wgpu-core/src/validation.rs index 58b93b00bc69..17d65ee2a042 100644 --- a/third_party/rust/wgpu-core/src/validation.rs +++ b/third_party/rust/wgpu-core/src/validation.rs @@ -1191,7 +1191,7 @@ impl Interface { ]; let total_invocations = entry_point.workgroup_size.iter().product::(); - if entry_point.workgroup_size.iter().any(|&s| s == 0) + if entry_point.workgroup_size.contains(&0) || total_invocations > self.limits.max_compute_invocations_per_workgroup || entry_point.workgroup_size[0] > max_workgroup_size_limits[0] || entry_point.workgroup_size[1] > max_workgroup_size_limits[1] diff --git a/third_party/rust/wgpu-hal/.cargo-checksum.json b/third_party/rust/wgpu-hal/.cargo-checksum.json index e48594c38661..9a5be10a57a5 100644 --- a/third_party/rust/wgpu-hal/.cargo-checksum.json +++ b/third_party/rust/wgpu-hal/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"f27848ebaa3b29a9857c545212793f99a020cd271907b504b16ed740691a08db","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","README.md":"cf9e84804a635e4a8a9fefc596be9da6bf7354dde0d105e27d56a12cb20dd8e3","build.rs":"40c99bddda32846afd639d84b9a160ddab338092560b5cb3402112ff37ab4fba","examples/halmark/main.rs":"a16c6902b2ad1a4d8db8d057091b47259d5f59e63058b07a4bada6c7defd9312","examples/halmark/shader.wgsl":"26c256ec36d6f0e9a1647431ca772766bee4382d64eaa718ba7b488dcfb6bcca","examples/raw-gles.em.html":"70fbe68394a1a4522192de1dcfaf7d399f60d7bdf5de70b708f9bb0417427546","examples/raw-gles.rs":"288dae4c701aa7e5ba498fdd03ea42e1046a27db0914d93c88a43a7953cb9064","examples/ray-traced-triangle/main.rs":"3d702095bd8295a0f762fded0e123845beb4d2c25bda2cf05c2de94a9420023c","examples/ray-traced-triangle/shader.wgsl":"cc10caf92746724a71f6dd0dbc3a71e57b37c7d1d83278556805a535c0728a9d","src/auxil/dxgi/conv.rs":"c7564baf7142ebcc1135e14f0c2600580edde8903279372bd25767fe532d20d3","src/auxil/dxgi/exception.rs":"7df3597c1fd6e7708f96a342846b87e6c6cc17fb9dbe09f6aec54984809a4818","src/auxil/dxgi/factory.rs":"fbae1cf83f4ac8cd356557fb3caa0da92c3968d68a36f5c8ea18b90b3bcaeeaa","src/auxil/dxgi/mod.rs":"56925eb2801c4abe80254ada791cd6587a97472f50df715534d61a362253e05b","src/auxil/dxgi/result.rs":"a14b8b0dd052e7dde11220043f180d2e8ce7ae522dea6e96536c82db13fc7abc","src/auxil/dxgi/time.rs":"1414a57363adf8381d89d9c1efdf9eee471081e1a2a583ae90f0310fd8902fd4","src/auxil/mod.rs":"540b9250d9f0e0af709245ce1e284eaca15b27d47550b0ebba2a512da1666c48","src/auxil/renderdoc.rs":"e687710ea0c9e88c9be0e2acd39d136635b835ffb941886b875811b3ec945080","src/dx12/adapter.rs":"ffcd8f8f429334c85a66c74795f2adbf2b03d4538af8fd70e480bd42fd9e6292","src/dx12/command.rs":"b348f945b4af33aecf667139c8ec7a697d5cf8cbb686c3220ac655c86164b876","src/dx12/conv.rs":"818bbbac8bb593fe1b0346b69cfd36c68e9a78e8be7e9f1ecac62a19133fc2ff","src/dx12/descriptor.rs":"fa523bb069b775065367a71edad7fbbfa2bad6f952a4781b048ecec0d35b70fb","src/dx12/device.rs":"c5343a6e783bdb5cf10beed9969261667ae244b74d2a081a246730da124a0c1e","src/dx12/instance.rs":"626ff3377fae82a356da3ebc66b6c453a6c9bfb0bb0cce51c4c3f20b2d9df964","src/dx12/mod.rs":"f1a1168520a77917e51ef226712bd5bc944e17af5455cfa40a4fab0121a78921","src/dx12/sampler.rs":"402b5850dc64ab23b760fa9ab44c229ed808bc4ab58f78de0b889b68a0e207c0","src/dx12/shader_compilation.rs":"32458bd5e63a855f24aea2345a4b65b3f2d1704a06480d6107ea247b6f0974b3","src/dx12/suballocation.rs":"ff637f0c1d618ea2abf259c13b7830ba15fba77d026c575835afa459261283b6","src/dx12/types.rs":"3fc7619fc09303eb3c936d4ded6889f94ce9e8b9aa62742ce900baa1b1e1cca7","src/dx12/view.rs":"3f4c23a34b86e3bfbcb11169a9dec5e1c21425e7ecad846b36c159ced0138cee","src/dynamic/adapter.rs":"e93f7d082a3950c9e8ccff8a631d251c7598b4b25dda9fe6347dadfa3ba07829","src/dynamic/command.rs":"a67394c333b2f0773717d64d7ca9a900868e7ce46dac832ec09a17a5a84bfa43","src/dynamic/device.rs":"230c48fba8e49cc46c420bcd1621beaf9802be9e65710cbfc2c1aea78e91b573","src/dynamic/instance.rs":"7b515c201e1ca24f24439544dbfa1d19ea1412a4f89bd803e009aed13b021e55","src/dynamic/mod.rs":"b02a3c11b22c896cf66ef206f5d4bb4e24988ecadc29972c572baf347f54aa04","src/dynamic/queue.rs":"d76abb4797e90253386d24584f186dbe1909e772560156b2e891fa043cfefbdc","src/dynamic/surface.rs":"4328c2fe86931f50aa00ac3d6982d0879b774eebf7a507903d1b1898c891fb4d","src/gles/adapter.rs":"e0187c442af127cb3940af9f1c5b27836a290bed706ca2177b7e03e1ae3021ef","src/gles/command.rs":"11becc2f9e6171b0cc90456d9c414b94e7f2152a53e4753c90aca3c42ab0204f","src/gles/conv.rs":"7f885dd2bc72641d22f8f2e688ebdd857663bfe315f1b5364ea302f99a05adbf","src/gles/device.rs":"d638f7e434005fe921922a75aa6d2404766bd687ecdc95f5eeb09079c5ea8bf6","src/gles/egl.rs":"c9737871f276c81f1994ebd55dabf8817cb46bdb4ecd495b1c28b52aba8f74fd","src/gles/emscripten.rs":"316d2bb6f2a4bb126dbe68a223f7393399080d116b61c39504454acdf4f9cfaf","src/gles/fence.rs":"083cd49747aba6272002aba0b0c37e5768cdbc2a1b8bacd1a244ee905d3f7b0f","src/gles/mod.rs":"346cbbe8bd070b24f36aefd3c425d8c255716a675a3ce182f12c39f580f6a077","src/gles/queue.rs":"992099d38a23d1f30292749932e627e74d490b62fbbba945b0349ac04d86dd88","src/gles/shaders/clear.frag":"9133ed8ed97d3641fbb6b5f5ea894a3554c629ccc1b80a5fc9221d7293aa1954","src/gles/shaders/clear.vert":"a543768725f4121ff2e9e1fb5b00644931e9d6f2f946c0ef01968afb5a135abd","src/gles/shaders/srgb_present.frag":"dd9a43c339a2fa4ccf7f6a1854c6f400cabf271a7d5e9230768e9f39d47f3ff5","src/gles/shaders/srgb_present.vert":"6e85d489403d80b81cc94790730bb53b309dfc5eeede8f1ea3412a660f31d357","src/gles/web.rs":"cb5940bf7b2381811675011b640040274f407a7d1908d0f82c813d6a9d3b00f7","src/gles/wgl.rs":"4242466a745e2263204e61b7de011c4a9a4c559ab06ae4df063685762b0cd042","src/lib.rs":"46ce110c98da4c32a678010f75d0b60bb44e55ed3edc139d8234d9e760d796b9","src/metal/adapter.rs":"6fef5b86aa923a75495adc5a51f4b9c7fa78da3194e7a58d3c6c527a2032f3f3","src/metal/command.rs":"e8af9260abc9852fce35d4dda981750c8f4c4d970ca9959e48442a036414aa8a","src/metal/conv.rs":"17f37cf5fea93b437e5ec9cfff9ae96e2c6a7f23fb2d3890616fb8dbeb5f13c4","src/metal/device.rs":"7874cfb8c865c2e65bfc07489b61a58f67a40974749b705a25172db444064ff5","src/metal/layer_observer.rs":"8370a6e443d01739b951b8538ee719a03b69fc0cbac92c748db418fbcc8837b5","src/metal/mod.rs":"d074363a2a7696dafac0c37d2460bbcb938b5a4b5029f8247a9574e2d42df819","src/metal/surface.rs":"828cf6f22bb5038afbd471d4164ecc256e832d51b0a845a12be839289f8bded8","src/metal/time.rs":"c32d69f30e846dfcc0e39e01097fb80df63b2bebb6586143bb62494999850246","src/noop/buffer.rs":"89a938411204b33082200a1306fe589587de15142392d7ec591676512e4cfeb2","src/noop/command.rs":"917b70ac315101633094b3f92744310a60940747a2cc8a9368141200ce0ed1fd","src/noop/mod.rs":"1af00e4b7bd2e57fec3c1dfbd56efd5acf3dfeb9f2d3618f91a6e40cdce6d59d","src/vulkan/adapter.rs":"db66ff6030749dc7960a5f8a224a6131bbb14c44f3ae238d6cb3b717a00c0c4d","src/vulkan/command.rs":"571662697bdfd6d8f5dada24a57dd72b6324f9f3abc787025adca67df7455cd4","src/vulkan/conv.rs":"ebb9bd12bfcf2c596d89c515b3d767241d61beb7cc138c61759a627183f23aa7","src/vulkan/device.rs":"6c69b59f703d119727727428a9bf389c242a2fb09628bcc4c91a1982e7fb0ee5","src/vulkan/drm.rs":"22aaa0644cf7e90840bce0377e8f990c0a839f46a054497dbb96105d59999022","src/vulkan/instance.rs":"0880893f69bc348cd1d5bcc00509d09e84e84960fe0c4ac2f9a2e4b0090d83a7","src/vulkan/mod.rs":"025833e3521186838a50219ef4a584fd7b388cc88a7901db75a33ad32790d66b","src/vulkan/sampler.rs":"77a91ec2e61481256eddcff7c4081d72b5cda09e2824333d77945da9eada8933"},"package":null} \ No newline at end of file +{"files":{"Cargo.toml":"ee5626ccef49ef8f7d59de0ddf7bc22bd03e26f1d375ddefacb2f840fb6d0e8b","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","README.md":"cf9e84804a635e4a8a9fefc596be9da6bf7354dde0d105e27d56a12cb20dd8e3","build.rs":"40c99bddda32846afd639d84b9a160ddab338092560b5cb3402112ff37ab4fba","examples/halmark/main.rs":"a16c6902b2ad1a4d8db8d057091b47259d5f59e63058b07a4bada6c7defd9312","examples/halmark/shader.wgsl":"26c256ec36d6f0e9a1647431ca772766bee4382d64eaa718ba7b488dcfb6bcca","examples/raw-gles.em.html":"70fbe68394a1a4522192de1dcfaf7d399f60d7bdf5de70b708f9bb0417427546","examples/raw-gles.rs":"288dae4c701aa7e5ba498fdd03ea42e1046a27db0914d93c88a43a7953cb9064","examples/ray-traced-triangle/main.rs":"3d702095bd8295a0f762fded0e123845beb4d2c25bda2cf05c2de94a9420023c","examples/ray-traced-triangle/shader.wgsl":"cc10caf92746724a71f6dd0dbc3a71e57b37c7d1d83278556805a535c0728a9d","src/auxil/dxgi/conv.rs":"c7564baf7142ebcc1135e14f0c2600580edde8903279372bd25767fe532d20d3","src/auxil/dxgi/exception.rs":"60fe0a6dbae2c3b133b75e268739a501ad1d207acfa5981532f1f704b1b8c47f","src/auxil/dxgi/factory.rs":"fbae1cf83f4ac8cd356557fb3caa0da92c3968d68a36f5c8ea18b90b3bcaeeaa","src/auxil/dxgi/mod.rs":"56925eb2801c4abe80254ada791cd6587a97472f50df715534d61a362253e05b","src/auxil/dxgi/result.rs":"a14b8b0dd052e7dde11220043f180d2e8ce7ae522dea6e96536c82db13fc7abc","src/auxil/dxgi/time.rs":"1414a57363adf8381d89d9c1efdf9eee471081e1a2a583ae90f0310fd8902fd4","src/auxil/mod.rs":"540b9250d9f0e0af709245ce1e284eaca15b27d47550b0ebba2a512da1666c48","src/auxil/renderdoc.rs":"e687710ea0c9e88c9be0e2acd39d136635b835ffb941886b875811b3ec945080","src/dx12/adapter.rs":"ffcd8f8f429334c85a66c74795f2adbf2b03d4538af8fd70e480bd42fd9e6292","src/dx12/command.rs":"b348f945b4af33aecf667139c8ec7a697d5cf8cbb686c3220ac655c86164b876","src/dx12/conv.rs":"818bbbac8bb593fe1b0346b69cfd36c68e9a78e8be7e9f1ecac62a19133fc2ff","src/dx12/descriptor.rs":"fa523bb069b775065367a71edad7fbbfa2bad6f952a4781b048ecec0d35b70fb","src/dx12/device.rs":"85ec2e4882eb80e7b321494cfb15d3aecee0866c9a4e6e18900be3939c3cac34","src/dx12/instance.rs":"626ff3377fae82a356da3ebc66b6c453a6c9bfb0bb0cce51c4c3f20b2d9df964","src/dx12/mod.rs":"f1a1168520a77917e51ef226712bd5bc944e17af5455cfa40a4fab0121a78921","src/dx12/sampler.rs":"402b5850dc64ab23b760fa9ab44c229ed808bc4ab58f78de0b889b68a0e207c0","src/dx12/shader_compilation.rs":"32458bd5e63a855f24aea2345a4b65b3f2d1704a06480d6107ea247b6f0974b3","src/dx12/suballocation.rs":"ff637f0c1d618ea2abf259c13b7830ba15fba77d026c575835afa459261283b6","src/dx12/types.rs":"3fc7619fc09303eb3c936d4ded6889f94ce9e8b9aa62742ce900baa1b1e1cca7","src/dx12/view.rs":"3f4c23a34b86e3bfbcb11169a9dec5e1c21425e7ecad846b36c159ced0138cee","src/dynamic/adapter.rs":"e93f7d082a3950c9e8ccff8a631d251c7598b4b25dda9fe6347dadfa3ba07829","src/dynamic/command.rs":"a67394c333b2f0773717d64d7ca9a900868e7ce46dac832ec09a17a5a84bfa43","src/dynamic/device.rs":"230c48fba8e49cc46c420bcd1621beaf9802be9e65710cbfc2c1aea78e91b573","src/dynamic/instance.rs":"7b515c201e1ca24f24439544dbfa1d19ea1412a4f89bd803e009aed13b021e55","src/dynamic/mod.rs":"b02a3c11b22c896cf66ef206f5d4bb4e24988ecadc29972c572baf347f54aa04","src/dynamic/queue.rs":"d76abb4797e90253386d24584f186dbe1909e772560156b2e891fa043cfefbdc","src/dynamic/surface.rs":"4328c2fe86931f50aa00ac3d6982d0879b774eebf7a507903d1b1898c891fb4d","src/gles/adapter.rs":"c84db856bc9d6adac2806d03487c7e7ccfa9235548b9d1583f11468bea1e3b75","src/gles/command.rs":"b4e0df28700b9709c24b14cec734cce7f256f6ade6220715e9bd71d4601daf77","src/gles/conv.rs":"7f885dd2bc72641d22f8f2e688ebdd857663bfe315f1b5364ea302f99a05adbf","src/gles/device.rs":"d638f7e434005fe921922a75aa6d2404766bd687ecdc95f5eeb09079c5ea8bf6","src/gles/egl.rs":"c9737871f276c81f1994ebd55dabf8817cb46bdb4ecd495b1c28b52aba8f74fd","src/gles/emscripten.rs":"316d2bb6f2a4bb126dbe68a223f7393399080d116b61c39504454acdf4f9cfaf","src/gles/fence.rs":"083cd49747aba6272002aba0b0c37e5768cdbc2a1b8bacd1a244ee905d3f7b0f","src/gles/mod.rs":"346cbbe8bd070b24f36aefd3c425d8c255716a675a3ce182f12c39f580f6a077","src/gles/queue.rs":"b4747ce2c1471a176d3b5205331735f0dd8a430572a3708e7e089bba32e10033","src/gles/shaders/clear.frag":"9133ed8ed97d3641fbb6b5f5ea894a3554c629ccc1b80a5fc9221d7293aa1954","src/gles/shaders/clear.vert":"a543768725f4121ff2e9e1fb5b00644931e9d6f2f946c0ef01968afb5a135abd","src/gles/shaders/srgb_present.frag":"dd9a43c339a2fa4ccf7f6a1854c6f400cabf271a7d5e9230768e9f39d47f3ff5","src/gles/shaders/srgb_present.vert":"6e85d489403d80b81cc94790730bb53b309dfc5eeede8f1ea3412a660f31d357","src/gles/web.rs":"cb5940bf7b2381811675011b640040274f407a7d1908d0f82c813d6a9d3b00f7","src/gles/wgl.rs":"4242466a745e2263204e61b7de011c4a9a4c559ab06ae4df063685762b0cd042","src/lib.rs":"46ce110c98da4c32a678010f75d0b60bb44e55ed3edc139d8234d9e760d796b9","src/metal/adapter.rs":"6fef5b86aa923a75495adc5a51f4b9c7fa78da3194e7a58d3c6c527a2032f3f3","src/metal/command.rs":"e8af9260abc9852fce35d4dda981750c8f4c4d970ca9959e48442a036414aa8a","src/metal/conv.rs":"17f37cf5fea93b437e5ec9cfff9ae96e2c6a7f23fb2d3890616fb8dbeb5f13c4","src/metal/device.rs":"7874cfb8c865c2e65bfc07489b61a58f67a40974749b705a25172db444064ff5","src/metal/layer_observer.rs":"8370a6e443d01739b951b8538ee719a03b69fc0cbac92c748db418fbcc8837b5","src/metal/mod.rs":"d074363a2a7696dafac0c37d2460bbcb938b5a4b5029f8247a9574e2d42df819","src/metal/surface.rs":"828cf6f22bb5038afbd471d4164ecc256e832d51b0a845a12be839289f8bded8","src/metal/time.rs":"c32d69f30e846dfcc0e39e01097fb80df63b2bebb6586143bb62494999850246","src/noop/buffer.rs":"89a938411204b33082200a1306fe589587de15142392d7ec591676512e4cfeb2","src/noop/command.rs":"917b70ac315101633094b3f92744310a60940747a2cc8a9368141200ce0ed1fd","src/noop/mod.rs":"1af00e4b7bd2e57fec3c1dfbd56efd5acf3dfeb9f2d3618f91a6e40cdce6d59d","src/vulkan/adapter.rs":"db66ff6030749dc7960a5f8a224a6131bbb14c44f3ae238d6cb3b717a00c0c4d","src/vulkan/command.rs":"73877c483de8d5e37ac56bcba6d8a8ffd7fe6a9fd85ae82be886794d0d99b50c","src/vulkan/conv.rs":"ebb9bd12bfcf2c596d89c515b3d767241d61beb7cc138c61759a627183f23aa7","src/vulkan/device.rs":"b659a02f9aab895863bda41fc964fff6b693921558ad361c2e1d4300c00730e5","src/vulkan/drm.rs":"22aaa0644cf7e90840bce0377e8f990c0a839f46a054497dbb96105d59999022","src/vulkan/instance.rs":"0880893f69bc348cd1d5bcc00509d09e84e84960fe0c4ac2f9a2e4b0090d83a7","src/vulkan/mod.rs":"60698c92f34552dadea242f34767e9ad6f17dd8527c7a9305595ad10945db323","src/vulkan/sampler.rs":"77a91ec2e61481256eddcff7c4081d72b5cda09e2824333d77945da9eada8933"},"package":null} \ No newline at end of file diff --git a/third_party/rust/wgpu-hal/Cargo.toml b/third_party/rust/wgpu-hal/Cargo.toml index 15002c3b57c7..8eebce7891d0 100644 --- a/third_party/rust/wgpu-hal/Cargo.toml +++ b/third_party/rust/wgpu-hal/Cargo.toml @@ -55,6 +55,7 @@ dx12 = [ "naga/hlsl-out", "dep:arrayvec", "dep:bit-set", + "dep:bytemuck", "dep:hashbrown", "dep:libloading", "dep:log", @@ -125,6 +126,7 @@ vulkan = [ "dep:android_system_properties", "dep:arrayvec", "dep:ash", + "dep:bytemuck", "dep:gpu-alloc", "dep:gpu-descriptor", "dep:hashbrown", @@ -166,9 +168,9 @@ version = "2.9" [dependencies.bytemuck] version = "1.22" features = [ - "derive", "extern_crate_alloc", "min_const_generics", + "derive", ] optional = true diff --git a/third_party/rust/wgpu-hal/src/auxil/dxgi/exception.rs b/third_party/rust/wgpu-hal/src/auxil/dxgi/exception.rs index 3700a24977db..f6c13a73e932 100644 --- a/third_party/rust/wgpu-hal/src/auxil/dxgi/exception.rs +++ b/third_party/rust/wgpu-hal/src/auxil/dxgi/exception.rs @@ -1,6 +1,5 @@ use std::{ borrow::Cow, - slice, string::{String, ToString as _}, }; @@ -48,18 +47,12 @@ unsafe extern "system" fn output_debug_string_handler( return Debug::EXCEPTION_CONTINUE_SEARCH; } let message = match record.ExceptionCode { - Foundation::DBG_PRINTEXCEPTION_C => String::from_utf8_lossy(unsafe { - slice::from_raw_parts( - record.ExceptionInformation[1] as *const u8, - record.ExceptionInformation[0], - ) - }), - Foundation::DBG_PRINTEXCEPTION_WIDE_C => Cow::Owned(String::from_utf16_lossy(unsafe { - slice::from_raw_parts( - record.ExceptionInformation[1] as *const u16, - record.ExceptionInformation[0], - ) - })), + Foundation::DBG_PRINTEXCEPTION_C => { + String::from_utf8_lossy(bytemuck::cast_slice(&record.ExceptionInformation)) + } + Foundation::DBG_PRINTEXCEPTION_WIDE_C => Cow::Owned(String::from_utf16_lossy( + bytemuck::cast_slice(&record.ExceptionInformation), + )), _ => return Debug::EXCEPTION_CONTINUE_SEARCH, }; diff --git a/third_party/rust/wgpu-hal/src/dx12/device.rs b/third_party/rust/wgpu-hal/src/dx12/device.rs index a3bfad335995..cdc641866559 100644 --- a/third_party/rust/wgpu-hal/src/dx12/device.rs +++ b/third_party/rust/wgpu-hal/src/dx12/device.rs @@ -2,13 +2,14 @@ use std::{ borrow::Cow, ffi, mem, num::NonZeroU32, - ptr, slice, + ptr, string::{String, ToString as _}, sync::Arc, time::{Duration, Instant}, vec::Vec, }; +use bytemuck::TransparentWrapper; use parking_lot::Mutex; use windows::{ core::Interface as _, @@ -2362,13 +2363,9 @@ impl crate::Device for super::Device { _bitfield2: 0, AccelerationStructure: instance.blas_address, }; - let temp: *const _ = &temp; - unsafe { - slice::from_raw_parts( - temp.cast::(), - size_of::(), - ) - .to_vec() - } + + wgt::bytemuck_wrapper!(unsafe struct Desc(Direct3D12::D3D12_RAYTRACING_INSTANCE_DESC)); + + bytemuck::bytes_of(&Desc::wrap(temp)).to_vec() } } diff --git a/third_party/rust/wgpu-hal/src/gles/adapter.rs b/third_party/rust/wgpu-hal/src/gles/adapter.rs index ff476645b8a7..a4370f81980d 100644 --- a/third_party/rust/wgpu-hal/src/gles/adapter.rs +++ b/third_party/rust/wgpu-hal/src/gles/adapter.rs @@ -377,8 +377,9 @@ impl super::Adapter { } else { vertex_shader_storage_textures.min(fragment_shader_storage_textures) }; - let indirect_execution = - supported((3, 1), (4, 3)) || extensions.contains("GL_ARB_multi_draw_indirect"); + // NOTE: GL_ARB_compute_shader adds support for indirect dispatch + let indirect_execution = supported((3, 1), (4, 3)) + || (extensions.contains("GL_ARB_draw_indirect") && supports_compute); let mut downlevel_flags = wgt::DownlevelFlags::empty() | wgt::DownlevelFlags::NON_POWER_OF_TWO_MIPMAPPED_TEXTURES diff --git a/third_party/rust/wgpu-hal/src/gles/command.rs b/third_party/rust/wgpu-hal/src/gles/command.rs index 9644018b371d..6cdf0fbf84fa 100644 --- a/third_party/rust/wgpu-hal/src/gles/command.rs +++ b/third_party/rust/wgpu-hal/src/gles/command.rs @@ -1,5 +1,5 @@ use alloc::string::String; -use core::{mem, ops::Range, slice}; +use core::{mem, ops::Range}; use arrayvec::ArrayVec; @@ -84,7 +84,7 @@ impl super::CommandBuffer { } fn add_push_constant_data(&mut self, data: &[u32]) -> Range { - let data_raw = unsafe { slice::from_raw_parts(data.as_ptr().cast(), size_of_val(data)) }; + let data_raw = bytemuck::cast_slice(data); let start = self.data_bytes.len(); assert!(start < u32::MAX as usize); self.data_bytes.extend_from_slice(data_raw); @@ -1199,7 +1199,7 @@ impl crate::CommandEncoder for super::CommandEncoder { unsafe fn dispatch(&mut self, count: [u32; 3]) { // Empty dispatches are invalid in OpenGL, but valid in WebGPU. - if count.iter().any(|&c| c == 0) { + if count.contains(&0) { return; } self.cmd_buffer.commands.push(C::Dispatch(count)); diff --git a/third_party/rust/wgpu-hal/src/gles/queue.rs b/third_party/rust/wgpu-hal/src/gles/queue.rs index 1d391f7bded6..65bfe526edf2 100644 --- a/third_party/rust/wgpu-hal/src/gles/queue.rs +++ b/third_party/rust/wgpu-hal/src/gles/queue.rs @@ -1,6 +1,6 @@ use alloc::sync::Arc; use alloc::vec; -use core::{slice, sync::atomic::Ordering}; +use core::sync::atomic::Ordering; use arrayvec::ArrayVec; use glow::HasContext; @@ -1039,12 +1039,7 @@ impl super::Queue { }; temp_query_results.push(result); } - let query_data = unsafe { - slice::from_raw_parts( - temp_query_results.as_ptr().cast::(), - temp_query_results.len() * size_of::(), - ) - }; + let query_data = bytemuck::cast_slice(&temp_query_results); match dst.raw { Some(buffer) => { unsafe { gl.bind_buffer(dst_target, Some(buffer)) }; diff --git a/third_party/rust/wgpu-hal/src/vulkan/command.rs b/third_party/rust/wgpu-hal/src/vulkan/command.rs index 03659efba33c..2d2798fb6c80 100644 --- a/third_party/rust/wgpu-hal/src/vulkan/command.rs +++ b/third_party/rust/wgpu-hal/src/vulkan/command.rs @@ -3,7 +3,7 @@ use super::conv; use arrayvec::ArrayVec; use ash::vk; -use std::{mem, ops::Range, slice}; +use std::{mem, ops::Range}; const ALLOCATION_GRANULARITY: u32 = 16; const DST_IMAGE_LAYOUT: vk::ImageLayout = vk::ImageLayout::TRANSFER_DST_OPTIMAL; @@ -900,7 +900,7 @@ impl crate::CommandEncoder for super::CommandEncoder { layout.raw, conv::map_shader_stage(stages), offset_bytes, - slice::from_raw_parts(data.as_ptr().cast(), data.len() * 4), + bytemuck::cast_slice(data), ) }; } diff --git a/third_party/rust/wgpu-hal/src/vulkan/device.rs b/third_party/rust/wgpu-hal/src/vulkan/device.rs index b71cd93e3d9c..c00792de9a68 100644 --- a/third_party/rust/wgpu-hal/src/vulkan/device.rs +++ b/third_party/rust/wgpu-hal/src/vulkan/device.rs @@ -4,7 +4,7 @@ use std::{ ffi::{CStr, CString}, mem::{self, MaybeUninit}, num::NonZeroU32, - ptr, slice, + ptr, sync::Arc, vec::Vec, }; @@ -2867,10 +2867,7 @@ impl crate::Device for super::Device { shader_binding_table_record_offset_and_flags: 0, acceleration_structure_reference: instance.blas_address, }; - let temp: *const _ = &temp; - unsafe { - slice::from_raw_parts::(temp.cast::(), size_of::()).to_vec() - } + bytemuck::bytes_of(&temp).to_vec() } } diff --git a/third_party/rust/wgpu-hal/src/vulkan/mod.rs b/third_party/rust/wgpu-hal/src/vulkan/mod.rs index ef349c7b2666..4ba4a6c3d989 100644 --- a/third_party/rust/wgpu-hal/src/vulkan/mod.rs +++ b/third_party/rust/wgpu-hal/src/vulkan/mod.rs @@ -47,6 +47,7 @@ use std::{ use arrayvec::ArrayVec; use ash::{ext, khr, vk}; +use bytemuck::{Pod, Zeroable}; use hashbrown::HashSet; use parking_lot::{Mutex, RwLock}; @@ -1489,7 +1490,7 @@ fn get_lost_err() -> crate::DeviceError { crate::DeviceError::Lost } -#[derive(Clone)] +#[derive(Clone, Copy, Pod, Zeroable)] #[repr(C)] struct RawTlasInstance { transform: [f32; 12], diff --git a/third_party/rust/wgpu-types/.cargo-checksum.json b/third_party/rust/wgpu-types/.cargo-checksum.json index 784f24a0d9bc..e83017e394cb 100644 --- a/third_party/rust/wgpu-types/.cargo-checksum.json +++ b/third_party/rust/wgpu-types/.cargo-checksum.json @@ -1 +1 @@ -{"files":{"Cargo.toml":"b751a810c5f9a71f0afa590df08c4bdc509ebe9ac17986494287db2d658fd16c","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","src/assertions.rs":"e4d2d40bc1e870a59637f4b9574743e19565a62f6dbcc21cb18a76b666b796eb","src/counters.rs":"e2a1c69126bdb6a35f74d5062e89e242eb955014d95c2b9e6e1b03f7b4b5bd98","src/env.rs":"26ffc91867625784159bcf391881187aa92cf92b81b1f40959ce1b96ae6d554d","src/features.rs":"1840b609306a7f8c433ebb0b654664f73e63d35c258b9c2f7848854b07f92462","src/instance.rs":"8eed48e71f40316d9fdf91fac4b0d14d46d301c3e3b13bbc702e6b1df8483b0c","src/lib.rs":"899b9bd381f8152b9b3c5814ccf03547f0eadae1d185055b3f5243d5c40749fc","src/math.rs":"3046121800bded318b7d219aea401907e7d3bba3b998df6745a71e76f0734de2"},"package":null} \ No newline at end of file +{"files":{"Cargo.toml":"491451d283f6070b5ca6a53c887af65dbe1c077163571ae7b132406b44bfc015","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","src/assertions.rs":"e4d2d40bc1e870a59637f4b9574743e19565a62f6dbcc21cb18a76b666b796eb","src/cast_utils.rs":"33f03a57ccbedef2699f2305bec584c623db1fd28bfdf584d1260da4fbecd529","src/counters.rs":"e2a1c69126bdb6a35f74d5062e89e242eb955014d95c2b9e6e1b03f7b4b5bd98","src/env.rs":"26ffc91867625784159bcf391881187aa92cf92b81b1f40959ce1b96ae6d554d","src/features.rs":"1840b609306a7f8c433ebb0b654664f73e63d35c258b9c2f7848854b07f92462","src/instance.rs":"544fb3cb41d88bd4926fd142d6435446ef033017936d56dbe028e02e64417fce","src/lib.rs":"9aabafba24ae163dbabe267d4f0d325c8cc95377921776bff1e48ce508d767e2","src/math.rs":"3046121800bded318b7d219aea401907e7d3bba3b998df6745a71e76f0734de2"},"package":null} \ No newline at end of file diff --git a/third_party/rust/wgpu-types/Cargo.toml b/third_party/rust/wgpu-types/Cargo.toml index 81753e2bb356..f09efdc2eef9 100644 --- a/third_party/rust/wgpu-types/Cargo.toml +++ b/third_party/rust/wgpu-types/Cargo.toml @@ -65,6 +65,14 @@ path = "src/lib.rs" version = "2.9" features = ["serde"] +[dependencies.bytemuck] +version = "1.22" +features = [ + "extern_crate_alloc", + "min_const_generics", + "derive", +] + [dependencies.log] version = "0.4" diff --git a/third_party/rust/wgpu-types/src/cast_utils.rs b/third_party/rust/wgpu-types/src/cast_utils.rs new file mode 100644 index 000000000000..9d7fc8009d01 --- /dev/null +++ b/third_party/rust/wgpu-types/src/cast_utils.rs @@ -0,0 +1,25 @@ +/// Wrapper to unsafely define a wrapper type that can be used with `bytemuck`'s traits. +/// +/// This is very useful as it allows us to use bytemuck on foreign types. Despite the +/// unsafe assertion, it means that bytemuck is handling all the actual casting, +/// so we can't screw up size or alignment handling. +/// +/// Once wrapped you can use the [`bytemuck::TransparentWrapper`] methods and +/// all the free methods that come with [`bytemuck::Pod`] and [`bytemuck::Zeroable`]. +/// +/// # Safety +/// +/// Once wrapped, the resulting type must follow all the invariants +/// of the [`bytemuck::Pod`] and [`bytemuck::Zeroable`] traits. +#[macro_export] +macro_rules! bytemuck_wrapper { + (unsafe struct $name:ident($inner:ty)) => { + #[derive(Copy, Clone)] + #[repr(transparent)] + struct $name($inner); + + unsafe impl bytemuck::Zeroable for $name {} + unsafe impl bytemuck::Pod for $name {} + unsafe impl bytemuck::TransparentWrapper<$inner> for $name {} + }; +} diff --git a/third_party/rust/wgpu-types/src/instance.rs b/third_party/rust/wgpu-types/src/instance.rs index 2083049918ce..28b29d3a04bb 100644 --- a/third_party/rust/wgpu-types/src/instance.rs +++ b/third_party/rust/wgpu-types/src/instance.rs @@ -5,7 +5,7 @@ use alloc::string::String; use crate::Backends; #[cfg(doc)] -use crate::Backend; +use crate::{Backend, DownlevelFlags}; /// Options for creating an instance. #[derive(Clone, Debug)] @@ -98,6 +98,11 @@ bitflags::bitflags! { /// /// When `Self::from_env()` is used takes value from `WGPU_GPU_BASED_VALIDATION` environment variable. const GPU_BASED_VALIDATION = 1 << 4; + + /// Validate indirect buffer content prior to issuing indirect draws/dispatches. + /// + /// When `Self::from_env()` is used takes value from `WGPU_VALIDATION_INDIRECT_CALL` environment variable. + const VALIDATION_INDIRECT_CALL = 1 << 5; } } @@ -111,7 +116,7 @@ impl InstanceFlags { /// Enable recommended debugging and validation flags. #[must_use] pub fn debugging() -> Self { - InstanceFlags::DEBUG | InstanceFlags::VALIDATION + InstanceFlags::DEBUG | InstanceFlags::VALIDATION | InstanceFlags::VALIDATION_INDIRECT_CALL } /// Enable advanced debugging and validation flags (potentially very slow). @@ -130,7 +135,7 @@ impl InstanceFlags { return InstanceFlags::debugging(); } - InstanceFlags::empty() + InstanceFlags::VALIDATION_INDIRECT_CALL } /// Derive defaults from environment variables. See [`Self::with_env()`] for more information. @@ -154,6 +159,7 @@ impl InstanceFlags { /// - `WGPU_DISCARD_HAL_LABELS` /// - `WGPU_ALLOW_UNDERLYING_NONCOMPLIANT_ADAPTER` /// - `WGPU_GPU_BASED_VALIDATION` + /// - `WGPU_VALIDATION_INDIRECT_CALL` #[must_use] pub fn with_env(mut self) -> Self { fn env(key: &str) -> Option { @@ -166,6 +172,7 @@ impl InstanceFlags { if let Some(bit) = env("WGPU_VALIDATION") { self.set(Self::VALIDATION, bit); } + if let Some(bit) = env("WGPU_DEBUG") { self.set(Self::DEBUG, bit); } @@ -178,6 +185,9 @@ impl InstanceFlags { if let Some(bit) = env("WGPU_GPU_BASED_VALIDATION") { self.set(Self::GPU_BASED_VALIDATION, bit); } + if let Some(bit) = env("WGPU_VALIDATION_INDIRECT_CALL") { + self.set(Self::VALIDATION_INDIRECT_CALL, bit); + } self } diff --git a/third_party/rust/wgpu-types/src/lib.rs b/third_party/rust/wgpu-types/src/lib.rs index e619bfd9dc14..b53d31871e5c 100644 --- a/third_party/rust/wgpu-types/src/lib.rs +++ b/third_party/rust/wgpu-types/src/lib.rs @@ -23,6 +23,8 @@ use core::{ ops::Range, }; +use bytemuck::{Pod, Zeroable}; + #[cfg(any(feature = "serde", test))] use { alloc::format, @@ -30,6 +32,7 @@ use { }; pub mod assertions; +mod cast_utils; mod counters; mod env; mod features; @@ -962,6 +965,8 @@ bitflags::bitflags! { const FRAGMENT_WRITABLE_STORAGE = 1 << 1; /// Supports indirect drawing and dispatching. /// + /// [`Self::COMPUTE_SHADERS`] must be present for this flag. + /// /// WebGL2, GLES 3.0, and Metal on Apple1/Apple2 GPUs do not support indirect. const INDIRECT_EXECUTION = 1 << 2; /// Supports non-zero `base_vertex` parameter to direct indexed draw calls. @@ -7214,7 +7219,7 @@ bitflags::bitflags! { /// Argument buffer layout for `draw_indirect` commands. #[repr(C)] -#[derive(Copy, Clone, Debug, Default)] +#[derive(Copy, Clone, Debug, Default, Pod, Zeroable)] pub struct DrawIndirectArgs { /// The number of vertices to draw. pub vertex_count: u32, @@ -7232,18 +7237,13 @@ impl DrawIndirectArgs { /// Returns the bytes representation of the struct, ready to be written in a buffer. #[must_use] pub fn as_bytes(&self) -> &[u8] { - unsafe { - core::mem::transmute(core::slice::from_raw_parts( - core::ptr::from_ref(self).cast::(), - size_of::(), - )) - } + bytemuck::bytes_of(self) } } /// Argument buffer layout for `draw_indexed_indirect` commands. #[repr(C)] -#[derive(Copy, Clone, Debug, Default)] +#[derive(Copy, Clone, Debug, Default, Pod, Zeroable)] pub struct DrawIndexedIndirectArgs { /// The number of indices to draw. pub index_count: u32, @@ -7263,18 +7263,13 @@ impl DrawIndexedIndirectArgs { /// Returns the bytes representation of the struct, ready to be written in a buffer. #[must_use] pub fn as_bytes(&self) -> &[u8] { - unsafe { - core::mem::transmute(core::slice::from_raw_parts( - core::ptr::from_ref(self).cast::(), - size_of::(), - )) - } + bytemuck::bytes_of(self) } } /// Argument buffer layout for `dispatch_indirect` commands. #[repr(C)] -#[derive(Copy, Clone, Debug, Default)] +#[derive(Copy, Clone, Debug, Default, Pod, Zeroable)] pub struct DispatchIndirectArgs { /// The number of work groups in X dimension. pub x: u32, @@ -7288,12 +7283,7 @@ impl DispatchIndirectArgs { /// Returns the bytes representation of the struct, ready to be written into a buffer. #[must_use] pub fn as_bytes(&self) -> &[u8] { - unsafe { - core::mem::transmute(core::slice::from_raw_parts( - core::ptr::from_ref(self).cast::(), - size_of::(), - )) - } + bytemuck::bytes_of(self) } }