Compare commits
1 commit
master
...
runtime-ge
Author | SHA1 | Date | |
---|---|---|---|
Jacob Lifshay | 86fc148806 |
|
@ -1,77 +0,0 @@
|
|||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
# See Notices.txt for copyright information
|
||||
on:
|
||||
workflow_call:
|
||||
outputs:
|
||||
cache-primary-key:
|
||||
value: ${{ jobs.deps.outputs.cache-primary-key }}
|
||||
|
||||
jobs:
|
||||
deps:
|
||||
runs-on: debian-12
|
||||
outputs:
|
||||
cache-primary-key: ${{ steps.restore-deps.outputs.cache-primary-key }}
|
||||
steps:
|
||||
- uses: https://code.forgejo.org/actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: https://code.forgejo.org/actions/cache/restore@v3
|
||||
id: restore-deps
|
||||
with:
|
||||
path: deps
|
||||
key: ${{ github.repository }}-deps-${{ runner.os }}-${{ hashFiles('.forgejo/workflows/deps.yml') }}
|
||||
lookup-only: true
|
||||
- name: Install Apt packages
|
||||
if: steps.restore-deps.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
apt-get update -qq
|
||||
apt-get install -qq \
|
||||
bison \
|
||||
build-essential \
|
||||
ccache \
|
||||
clang \
|
||||
cvc5 \
|
||||
flex \
|
||||
gawk \
|
||||
g++ \
|
||||
git \
|
||||
libboost-filesystem-dev \
|
||||
libboost-python-dev \
|
||||
libboost-system-dev \
|
||||
libffi-dev \
|
||||
libreadline-dev \
|
||||
lld \
|
||||
pkg-config \
|
||||
python3 \
|
||||
python3-click \
|
||||
tcl-dev \
|
||||
zlib1g-dev
|
||||
- name: Install Firtool
|
||||
if: steps.restore-deps.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
mkdir -p deps
|
||||
wget -O deps/firrtl.tar.gz https://github.com/llvm/circt/releases/download/firtool-1.86.0/firrtl-bin-linux-x64.tar.gz
|
||||
sha256sum -c - <<<'bf6f4ab18ae76f135c944efbd81e25391c31c1bd0617c58ab0592640abefee14 deps/firrtl.tar.gz'
|
||||
tar -C deps -xvaf deps/firrtl.tar.gz
|
||||
rm -rf deps/firtool
|
||||
mv deps/firtool-1.86.0 deps/firtool
|
||||
- name: Get SymbiYosys
|
||||
if: steps.restore-deps.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
git clone --depth=1 --branch=yosys-0.45 https://github.com/YosysHQ/sby.git deps/sby
|
||||
- name: Build Z3
|
||||
if: steps.restore-deps.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
git clone --depth=1 --recursive --branch=z3-4.13.3 https://github.com/Z3Prover/z3.git deps/z3
|
||||
(cd deps/z3; PYTHON=python3 ./configure --prefix=/usr/local)
|
||||
make -C deps/z3/build -j"$(nproc)"
|
||||
- name: Build Yosys
|
||||
if: steps.restore-deps.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
git clone --depth=1 --recursive --branch=0.45 https://github.com/YosysHQ/yosys.git deps/yosys
|
||||
make -C deps/yosys -j"$(nproc)"
|
||||
- uses: https://code.forgejo.org/actions/cache/save@v3
|
||||
if: steps.restore-deps.outputs.cache-hit != 'true'
|
||||
with:
|
||||
path: deps
|
||||
key: ${{ steps.restore-deps.outputs.cache-primary-key }}
|
|
@ -1,60 +1,19 @@
|
|||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
# See Notices.txt for copyright information
|
||||
on: [push, pull_request]
|
||||
|
||||
jobs:
|
||||
deps:
|
||||
uses: ./.forgejo/workflows/deps.yml
|
||||
test:
|
||||
runs-on: debian-12
|
||||
needs: deps
|
||||
steps:
|
||||
- uses: https://code.forgejo.org/actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- run: |
|
||||
scripts/check-copyright.sh
|
||||
- run: |
|
||||
apt-get update -qq
|
||||
apt-get install -qq \
|
||||
bison \
|
||||
build-essential \
|
||||
ccache \
|
||||
clang \
|
||||
cvc5 \
|
||||
flex \
|
||||
gawk \
|
||||
git \
|
||||
libboost-filesystem-dev \
|
||||
libboost-python-dev \
|
||||
libboost-system-dev \
|
||||
libffi-dev \
|
||||
libreadline-dev \
|
||||
lld \
|
||||
pkg-config \
|
||||
python3 \
|
||||
python3-click \
|
||||
tcl-dev \
|
||||
z3 \
|
||||
zlib1g-dev
|
||||
- run: |
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.80.1
|
||||
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain 1.79.0
|
||||
source "$HOME/.cargo/env"
|
||||
echo "$PATH" >> "$GITHUB_PATH"
|
||||
- uses: https://code.forgejo.org/actions/cache/restore@v3
|
||||
with:
|
||||
path: deps
|
||||
key: ${{ needs.deps.outputs.cache-primary-key }}
|
||||
fail-on-cache-miss: true
|
||||
- run: |
|
||||
make -C deps/z3/build install
|
||||
make -C deps/sby install
|
||||
make -C deps/yosys install
|
||||
export PATH="$(realpath deps/firtool/bin):$PATH"
|
||||
echo "$PATH" >> "$GITHUB_PATH"
|
||||
- uses: https://github.com/Swatinem/rust-cache@v2
|
||||
with:
|
||||
save-if: ${{ github.ref == 'refs/heads/master' }}
|
||||
- run: cargo test
|
||||
- run: cargo build --tests --features=unstable-doc
|
||||
- run: cargo test --features=unstable-doc
|
||||
- run: cargo doc --features=unstable-doc
|
||||
|
|
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,4 +1,2 @@
|
|||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
# See Notices.txt for copyright information
|
||||
/target
|
||||
.vscode
|
||||
.vscode
|
188
Cargo.lock
generated
188
Cargo.lock
generated
|
@ -56,7 +56,7 @@ version = "1.1.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -66,21 +66,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
|
@ -121,20 +109,6 @@ dependencies = [
|
|||
"wyz",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blake3"
|
||||
version = "1.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d82033247fd8e890df8f740e407ad4d038debb9eb1f40533fffb32e7d17dc6f7"
|
||||
dependencies = [
|
||||
"arrayref",
|
||||
"arrayvec",
|
||||
"cc",
|
||||
"cfg-if",
|
||||
"constant_time_eq",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
|
@ -144,15 +118,6 @@ dependencies = [
|
|||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.1.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e80e3b6a3ab07840e1cae9b0666a63970dc28e8ed5ffbcdacbfc760c281bfc1"
|
||||
dependencies = [
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
|
@ -205,12 +170,6 @@ version = "1.0.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422"
|
||||
|
||||
[[package]]
|
||||
name = "constant_time_eq"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.12"
|
||||
|
@ -230,27 +189,6 @@ dependencies = [
|
|||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctor"
|
||||
version = "0.2.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edb49164822f3ee45b17acd4a208cfc1251410cf0cad9a833234c9890774dd9f"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_destructure2"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64b697ac90ff296f0fc031ee5a61c7ac31fb9fff50e3fb32873b09223613fc0c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
|
@ -280,7 +218,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -301,37 +239,32 @@ checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
|
|||
|
||||
[[package]]
|
||||
name = "fayalite"
|
||||
version = "0.2.1"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"bitvec",
|
||||
"blake3",
|
||||
"clap",
|
||||
"ctor",
|
||||
"eyre",
|
||||
"fayalite-proc-macros",
|
||||
"fayalite-visit-gen",
|
||||
"hashbrown",
|
||||
"jobslot",
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"os_pipe",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"tempfile",
|
||||
"trybuild",
|
||||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fayalite-proc-macros"
|
||||
version = "0.2.1"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"fayalite-proc-macros-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fayalite-proc-macros-impl"
|
||||
version = "0.2.1"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"num-bigint",
|
||||
|
@ -345,7 +278,7 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "fayalite-visit-gen"
|
||||
version = "0.2.1"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"prettyplease",
|
||||
|
@ -373,17 +306,6 @@ dependencies = [
|
|||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.1"
|
||||
|
@ -412,7 +334,7 @@ version = "0.5.9"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5"
|
||||
dependencies = [
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -444,20 +366,6 @@ version = "1.0.10"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c"
|
||||
|
||||
[[package]]
|
||||
name = "jobslot"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fe10868679d7a24c2c67d862d0e64a342ce9aef7cdde9ce8019bd35d353d458d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"derive_destructure2",
|
||||
"getrandom",
|
||||
"libc",
|
||||
"scopeguard",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.153"
|
||||
|
@ -505,16 +413,6 @@ version = "1.19.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
|
||||
|
||||
[[package]]
|
||||
name = "os_pipe"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5ffd2b0a5634335b135d5728d84c5e0fd726954b87111f7506a61c502280d982"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "prettyplease"
|
||||
version = "0.2.20"
|
||||
|
@ -559,7 +457,7 @@ dependencies = [
|
|||
"errno",
|
||||
"libc",
|
||||
"linux-raw-sys",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -568,12 +466,6 @@ version = "1.0.17"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.202"
|
||||
|
@ -617,12 +509,6 @@ dependencies = [
|
|||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "strsim"
|
||||
version = "0.11.1"
|
||||
|
@ -655,7 +541,7 @@ dependencies = [
|
|||
"cfg-if",
|
||||
"fastrand",
|
||||
"rustix",
|
||||
"windows-sys 0.52.0",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -726,12 +612,6 @@ version = "0.9.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "6.0.1"
|
||||
|
@ -784,25 +664,15 @@ dependencies = [
|
|||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
|
||||
checksum = "7dd37b7e5ab9018759f893a1952c9420d060016fc19a472b4bb20d1bdd694d1b"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm",
|
||||
"windows_aarch64_msvc",
|
||||
"windows_i686_gnu",
|
||||
"windows_i686_gnullvm",
|
||||
"windows_i686_msvc",
|
||||
"windows_x86_64_gnu",
|
||||
"windows_x86_64_gnullvm",
|
||||
|
@ -811,51 +681,45 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
|
||||
checksum = "bcf46cf4c365c6f2d1cc93ce535f2c8b244591df96ceee75d8e83deb70a9cac9"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
|
||||
checksum = "da9f259dd3bcf6990b55bffd094c4f7235817ba4ceebde8e6d11cd0c5633b675"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnullvm"
|
||||
version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
|
||||
checksum = "b474d8268f99e0995f25b9f095bc7434632601028cf86590aea5c8a5cb7801d3"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
|
||||
checksum = "1515e9a29e5bed743cb4415a9ecf5dfca648ce85ee42e15873c3cd8610ff8e02"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
|
||||
checksum = "5eee091590e89cc02ad514ffe3ead9eb6b660aedca2183455434b93546371a03"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
|
||||
checksum = "77ca79f2451b49fa9e2af39f0747fe999fcda4f5e241b2898624dca97a1f2177"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
version = "0.52.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
checksum = "32b752e52a2da0ddfbdbcc6fceadfeede4c939ed16d13e648833a61dfb611ed8"
|
||||
|
||||
[[package]]
|
||||
name = "winsafe"
|
||||
|
|
17
Cargo.toml
17
Cargo.toml
|
@ -5,30 +5,24 @@ resolver = "2"
|
|||
members = ["crates/*"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.2.1"
|
||||
version = "0.2.0"
|
||||
license = "LGPL-3.0-or-later"
|
||||
edition = "2021"
|
||||
repository = "https://git.libre-chip.org/libre-chip/fayalite"
|
||||
keywords = ["hdl", "hardware", "semiconductors", "firrtl", "fpga"]
|
||||
categories = ["simulation", "development-tools", "compilers"]
|
||||
rust-version = "1.80.1"
|
||||
rust-version = "1.79"
|
||||
|
||||
[workspace.dependencies]
|
||||
fayalite-proc-macros = { version = "=0.2.1", path = "crates/fayalite-proc-macros" }
|
||||
fayalite-proc-macros-impl = { version = "=0.2.1", path = "crates/fayalite-proc-macros-impl" }
|
||||
fayalite-visit-gen = { version = "=0.2.1", path = "crates/fayalite-visit-gen" }
|
||||
fayalite-proc-macros = { version = "=0.2.0", path = "crates/fayalite-proc-macros" }
|
||||
fayalite-proc-macros-impl = { version = "=0.2.0", path = "crates/fayalite-proc-macros-impl" }
|
||||
fayalite-visit-gen = { version = "=0.2.0", path = "crates/fayalite-visit-gen" }
|
||||
base16ct = "0.2.0"
|
||||
bitvec = { version = "1.0.1", features = ["serde"] }
|
||||
blake3 = { version = "1.5.4", features = ["serde"] }
|
||||
clap = { version = "4.5.9", features = ["derive", "env", "string"] }
|
||||
ctor = "0.2.8"
|
||||
eyre = "0.6.12"
|
||||
hashbrown = "0.14.3"
|
||||
indexmap = { version = "2.2.6", features = ["serde"] }
|
||||
jobslot = "0.2.19"
|
||||
num-bigint = "0.4.4"
|
||||
num-traits = "0.2.16"
|
||||
os_pipe = "1.2.1"
|
||||
prettyplease = "0.2.20"
|
||||
proc-macro2 = "1.0.83"
|
||||
quote = "1.0.36"
|
||||
|
@ -39,4 +33,3 @@ syn = { version = "2.0.66", features = ["full", "fold", "visit", "extra-traits"]
|
|||
tempfile = "3.10.1"
|
||||
thiserror = "1.0.61"
|
||||
trybuild = "1.0"
|
||||
which = "6.0.1"
|
||||
|
|
|
@ -1,7 +1,3 @@
|
|||
<!--
|
||||
SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
See Notices.txt for copyright information
|
||||
-->
|
||||
# Fayalite
|
||||
|
||||
Fayalite is a library for designing digital hardware -- a hardware description language (HDL) embedded in the Rust programming language. Fayalite's semantics are based on [FIRRTL] as interpreted by [LLVM CIRCT](https://circt.llvm.org/docs/Dialects/FIRRTL/FIRRTLAnnotations/).
|
||||
|
|
|
@ -13,11 +13,11 @@ rust-version.workspace = true
|
|||
version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
base16ct.workspace = true
|
||||
num-bigint.workspace = true
|
||||
prettyplease.workspace = true
|
||||
proc-macro2.workspace = true
|
||||
quote.workspace = true
|
||||
sha2.workspace = true
|
||||
syn.workspace = true
|
||||
tempfile.workspace = true
|
||||
base16ct = { workspace = true }
|
||||
num-bigint = { workspace = true }
|
||||
prettyplease = { workspace = true }
|
||||
proc-macro2 = { workspace = true }
|
||||
quote = { workspace = true }
|
||||
sha2 = { workspace = true }
|
||||
syn = { workspace = true }
|
||||
tempfile = { workspace = true }
|
||||
|
|
|
@ -224,31 +224,25 @@ forward_fold!(syn::ExprPath => fold_expr_path);
|
|||
forward_fold!(syn::ExprRepeat => fold_expr_repeat);
|
||||
forward_fold!(syn::ExprStruct => fold_expr_struct);
|
||||
forward_fold!(syn::ExprTuple => fold_expr_tuple);
|
||||
forward_fold!(syn::FieldPat => fold_field_pat);
|
||||
forward_fold!(syn::Ident => fold_ident);
|
||||
forward_fold!(syn::Member => fold_member);
|
||||
forward_fold!(syn::Path => fold_path);
|
||||
forward_fold!(syn::Type => fold_type);
|
||||
forward_fold!(syn::TypePath => fold_type_path);
|
||||
forward_fold!(syn::WherePredicate => fold_where_predicate);
|
||||
no_op_fold!(proc_macro2::Span);
|
||||
no_op_fold!(syn::parse::Nothing);
|
||||
no_op_fold!(syn::token::Brace);
|
||||
no_op_fold!(syn::token::Bracket);
|
||||
no_op_fold!(syn::token::Group);
|
||||
no_op_fold!(syn::token::Paren);
|
||||
no_op_fold!(syn::Token![_]);
|
||||
no_op_fold!(syn::Token![,]);
|
||||
no_op_fold!(syn::Token![;]);
|
||||
no_op_fold!(syn::Token![:]);
|
||||
no_op_fold!(syn::Token![::]);
|
||||
no_op_fold!(syn::Token![..]);
|
||||
no_op_fold!(syn::Token![.]);
|
||||
no_op_fold!(syn::Token![#]);
|
||||
no_op_fold!(syn::Token![<]);
|
||||
no_op_fold!(syn::Token![=]);
|
||||
no_op_fold!(syn::Token![=>]);
|
||||
no_op_fold!(syn::Token![>]);
|
||||
no_op_fold!(syn::Token![|]);
|
||||
no_op_fold!(syn::Token![enum]);
|
||||
no_op_fold!(syn::Token![extern]);
|
||||
|
@ -257,4 +251,3 @@ no_op_fold!(syn::Token![mut]);
|
|||
no_op_fold!(syn::Token![static]);
|
||||
no_op_fold!(syn::Token![struct]);
|
||||
no_op_fold!(syn::Token![where]);
|
||||
no_op_fold!(usize);
|
||||
|
|
|
@ -1,859 +0,0 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
hdl_type_common::{
|
||||
common_derives, get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedField,
|
||||
ParsedFieldsNamed, ParsedGenerics, SplitForImpl, TypesParser, WrappedInConst,
|
||||
},
|
||||
kw, Errors, HdlAttr, PairsIterExt,
|
||||
};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote_spanned, ToTokens};
|
||||
use syn::{
|
||||
parse_quote, parse_quote_spanned,
|
||||
punctuated::{Pair, Punctuated},
|
||||
spanned::Spanned,
|
||||
token::Brace,
|
||||
AngleBracketedGenericArguments, Attribute, Field, FieldMutability, Fields, FieldsNamed,
|
||||
GenericParam, Generics, Ident, ItemStruct, Path, Token, Type, Visibility,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct ParsedBundle {
|
||||
pub(crate) attrs: Vec<Attribute>,
|
||||
pub(crate) options: HdlAttr<ItemOptions, kw::hdl>,
|
||||
pub(crate) vis: Visibility,
|
||||
pub(crate) struct_token: Token![struct],
|
||||
pub(crate) ident: Ident,
|
||||
pub(crate) generics: MaybeParsed<ParsedGenerics, Generics>,
|
||||
pub(crate) fields: MaybeParsed<ParsedFieldsNamed, FieldsNamed>,
|
||||
pub(crate) field_flips: Vec<Option<HdlAttr<kw::flip, kw::hdl>>>,
|
||||
pub(crate) mask_type_ident: Ident,
|
||||
pub(crate) mask_type_match_variant_ident: Ident,
|
||||
pub(crate) match_variant_ident: Ident,
|
||||
pub(crate) builder_ident: Ident,
|
||||
pub(crate) mask_type_builder_ident: Ident,
|
||||
}
|
||||
|
||||
impl ParsedBundle {
|
||||
fn parse_field(
|
||||
errors: &mut Errors,
|
||||
field: &mut Field,
|
||||
index: usize,
|
||||
) -> Option<HdlAttr<kw::flip, kw::hdl>> {
|
||||
let Field {
|
||||
attrs,
|
||||
vis: _,
|
||||
mutability,
|
||||
ident,
|
||||
colon_token,
|
||||
ty,
|
||||
} = field;
|
||||
let ident = ident.get_or_insert_with(|| format_ident!("_{}", index, span = ty.span()));
|
||||
if !matches!(mutability, FieldMutability::None) {
|
||||
// FIXME: use mutability as the spanned tokens,
|
||||
// blocked on https://github.com/dtolnay/syn/issues/1717
|
||||
errors.error(&ident, "field mutability is not supported");
|
||||
*mutability = FieldMutability::None;
|
||||
}
|
||||
*mutability = FieldMutability::None;
|
||||
colon_token.get_or_insert(Token![:](ident.span()));
|
||||
errors.unwrap_or_default(HdlAttr::parse_and_take_attr(attrs))
|
||||
}
|
||||
fn parse(item: ItemStruct) -> syn::Result<Self> {
|
||||
let ItemStruct {
|
||||
mut attrs,
|
||||
vis,
|
||||
struct_token,
|
||||
ident,
|
||||
mut generics,
|
||||
fields,
|
||||
semi_token,
|
||||
} = item;
|
||||
let mut errors = Errors::new();
|
||||
let mut options = errors
|
||||
.unwrap_or_default(HdlAttr::<ItemOptions, kw::hdl>::parse_and_take_attr(
|
||||
&mut attrs,
|
||||
))
|
||||
.unwrap_or_default();
|
||||
errors.ok(options.body.validate());
|
||||
let ItemOptions {
|
||||
outline_generated: _,
|
||||
target: _,
|
||||
custom_bounds,
|
||||
no_static: _,
|
||||
no_runtime_generics: _,
|
||||
} = options.body;
|
||||
let mut fields = match fields {
|
||||
syn::Fields::Named(fields) => fields,
|
||||
syn::Fields::Unnamed(fields) => {
|
||||
errors.error(&fields, "#[hdl] struct must use curly braces: {}");
|
||||
FieldsNamed {
|
||||
brace_token: Brace(fields.paren_token.span),
|
||||
named: fields.unnamed,
|
||||
}
|
||||
}
|
||||
syn::Fields::Unit => {
|
||||
errors.error(&fields, "#[hdl] struct must use curly braces: {}");
|
||||
FieldsNamed {
|
||||
brace_token: Brace(semi_token.unwrap_or_default().span),
|
||||
named: Punctuated::default(),
|
||||
}
|
||||
}
|
||||
};
|
||||
let mut field_flips = Vec::with_capacity(fields.named.len());
|
||||
for (index, field) in fields.named.iter_mut().enumerate() {
|
||||
field_flips.push(Self::parse_field(&mut errors, field, index));
|
||||
}
|
||||
let generics = if custom_bounds.is_some() {
|
||||
MaybeParsed::Unrecognized(generics)
|
||||
} else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) {
|
||||
MaybeParsed::Parsed(generics)
|
||||
} else {
|
||||
MaybeParsed::Unrecognized(generics)
|
||||
};
|
||||
let fields = TypesParser::maybe_run(generics.as_ref(), fields, &mut errors);
|
||||
errors.finish()?;
|
||||
Ok(Self {
|
||||
attrs,
|
||||
options,
|
||||
vis,
|
||||
struct_token,
|
||||
generics,
|
||||
fields,
|
||||
field_flips,
|
||||
mask_type_ident: format_ident!("__{}__MaskType", ident),
|
||||
mask_type_match_variant_ident: format_ident!("__{}__MaskType__MatchVariant", ident),
|
||||
match_variant_ident: format_ident!("__{}__MatchVariant", ident),
|
||||
mask_type_builder_ident: format_ident!("__{}__MaskType__Builder", ident),
|
||||
builder_ident: format_ident!("__{}__Builder", ident),
|
||||
ident,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
struct Builder {
|
||||
vis: Visibility,
|
||||
struct_token: Token![struct],
|
||||
ident: Ident,
|
||||
target: Path,
|
||||
generics: Generics,
|
||||
fields: FieldsNamed,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
|
||||
enum BuilderFieldState {
|
||||
Unfilled,
|
||||
Generic,
|
||||
Filled,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
fn phantom_field_name(&self) -> Ident {
|
||||
format_ident!("__phantom", span = self.ident.span())
|
||||
}
|
||||
fn phantom_field(&self) -> Field {
|
||||
let target = &self.target;
|
||||
let type_generics = self.generics.split_for_impl().1;
|
||||
Field {
|
||||
attrs: vec![],
|
||||
vis: Visibility::Inherited,
|
||||
mutability: FieldMutability::None,
|
||||
ident: Some(self.phantom_field_name()),
|
||||
colon_token: Some(Token![:](self.ident.span())),
|
||||
ty: parse_quote_spanned! {self.ident.span()=>
|
||||
::fayalite::__std::marker::PhantomData<#target #type_generics>
|
||||
},
|
||||
}
|
||||
}
|
||||
fn builder_struct_generics(
|
||||
&self,
|
||||
mut get_field_state: impl FnMut(usize) -> BuilderFieldState,
|
||||
) -> Generics {
|
||||
let mut retval = self.generics.clone();
|
||||
for param in retval.params.iter_mut() {
|
||||
match param {
|
||||
GenericParam::Lifetime(_) => {}
|
||||
GenericParam::Type(param) => param.default = None,
|
||||
GenericParam::Const(param) => param.default = None,
|
||||
}
|
||||
}
|
||||
for (field_index, field) in self.fields.named.iter().enumerate() {
|
||||
match get_field_state(field_index) {
|
||||
BuilderFieldState::Unfilled | BuilderFieldState::Filled => continue,
|
||||
BuilderFieldState::Generic => {}
|
||||
}
|
||||
if !retval.params.empty_or_trailing() {
|
||||
retval.params.push_punct(Token![,](self.ident.span()));
|
||||
}
|
||||
retval.params.push_value(GenericParam::Type(
|
||||
type_var_for_field_name(field.ident.as_ref().unwrap()).into(),
|
||||
));
|
||||
}
|
||||
retval
|
||||
}
|
||||
fn builder_struct_ty(
|
||||
&self,
|
||||
mut get_field_state: impl FnMut(usize) -> BuilderFieldState,
|
||||
) -> Type {
|
||||
let mut ty_arguments: AngleBracketedGenericArguments = if self.generics.params.is_empty() {
|
||||
parse_quote_spanned! {self.ident.span()=>
|
||||
<>
|
||||
}
|
||||
} else {
|
||||
let builder_type_generics = self.generics.split_for_impl().1;
|
||||
parse_quote! { #builder_type_generics }
|
||||
};
|
||||
for (field_index, Field { ident, ty, .. }) in self.fields.named.iter().enumerate() {
|
||||
let ident = ident.as_ref().unwrap();
|
||||
if !ty_arguments.args.empty_or_trailing() {
|
||||
ty_arguments.args.push_punct(Token![,](self.ident.span()));
|
||||
}
|
||||
ty_arguments
|
||||
.args
|
||||
.push_value(match get_field_state(field_index) {
|
||||
BuilderFieldState::Unfilled => parse_quote_spanned! {self.ident.span()=>
|
||||
::fayalite::bundle::Unfilled<#ty>
|
||||
},
|
||||
BuilderFieldState::Generic => {
|
||||
let type_var = type_var_for_field_name(ident);
|
||||
parse_quote_spanned! {self.ident.span()=>
|
||||
#type_var
|
||||
}
|
||||
}
|
||||
BuilderFieldState::Filled => parse_quote_spanned! {self.ident.span()=>
|
||||
::fayalite::expr::Expr<#ty>
|
||||
},
|
||||
});
|
||||
}
|
||||
let ident = &self.ident;
|
||||
parse_quote_spanned! {ident.span()=>
|
||||
#ident #ty_arguments
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn type_var_for_field_name(ident: &Ident) -> Ident {
|
||||
format_ident!("__T_{}", ident)
|
||||
}
|
||||
|
||||
fn field_fn_for_field_name(ident: &Ident) -> Ident {
|
||||
format_ident!("field_{}", ident)
|
||||
}
|
||||
|
||||
impl ToTokens for Builder {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self {
|
||||
vis,
|
||||
struct_token,
|
||||
ident,
|
||||
target,
|
||||
generics: _,
|
||||
fields,
|
||||
} = self;
|
||||
let phantom_field_name = self.phantom_field_name();
|
||||
let builder_struct = ItemStruct {
|
||||
attrs: vec![parse_quote_spanned! {ident.span()=>
|
||||
#[allow(non_camel_case_types, dead_code)]
|
||||
}],
|
||||
vis: vis.clone(),
|
||||
struct_token: *struct_token,
|
||||
ident: ident.clone(),
|
||||
generics: self.builder_struct_generics(|_| BuilderFieldState::Generic),
|
||||
fields: Fields::Named(FieldsNamed {
|
||||
brace_token: fields.brace_token,
|
||||
named: Punctuated::from_iter(
|
||||
[Pair::Punctuated(
|
||||
self.phantom_field(),
|
||||
Token![,](self.ident.span()),
|
||||
)]
|
||||
.into_iter()
|
||||
.chain(fields.named.pairs().map_pair_value_ref(|field| {
|
||||
let ident = field.ident.as_ref().unwrap();
|
||||
let type_var = type_var_for_field_name(ident);
|
||||
Field {
|
||||
vis: Visibility::Inherited,
|
||||
ty: parse_quote_spanned! {ident.span()=>
|
||||
#type_var
|
||||
},
|
||||
..field.clone()
|
||||
}
|
||||
})),
|
||||
),
|
||||
}),
|
||||
semi_token: None,
|
||||
};
|
||||
builder_struct.to_tokens(tokens);
|
||||
let field_idents = Vec::from_iter(
|
||||
self.fields
|
||||
.named
|
||||
.iter()
|
||||
.map(|field| field.ident.as_ref().unwrap()),
|
||||
);
|
||||
for (
|
||||
field_index,
|
||||
Field {
|
||||
vis,
|
||||
ident: field_ident,
|
||||
ty,
|
||||
..
|
||||
},
|
||||
) in self.fields.named.iter().enumerate()
|
||||
{
|
||||
let field_ident = field_ident.as_ref().unwrap();
|
||||
let fn_ident = field_fn_for_field_name(field_ident);
|
||||
let fn_generics = self.builder_struct_generics(|i| {
|
||||
if i == field_index {
|
||||
BuilderFieldState::Unfilled
|
||||
} else {
|
||||
BuilderFieldState::Generic
|
||||
}
|
||||
});
|
||||
let (impl_generics, _, where_clause) = fn_generics.split_for_impl();
|
||||
let unfilled_ty = self.builder_struct_ty(|i| {
|
||||
if i == field_index {
|
||||
BuilderFieldState::Unfilled
|
||||
} else {
|
||||
BuilderFieldState::Generic
|
||||
}
|
||||
});
|
||||
let filled_ty = self.builder_struct_ty(|i| {
|
||||
if i == field_index {
|
||||
BuilderFieldState::Filled
|
||||
} else {
|
||||
BuilderFieldState::Generic
|
||||
}
|
||||
});
|
||||
let pat_fields =
|
||||
Vec::from_iter(self.fields.named.iter().enumerate().map(|(i, field)| {
|
||||
let field_ident = field.ident.as_ref().unwrap();
|
||||
if field_index == i {
|
||||
quote_spanned! {self.ident.span()=>
|
||||
#field_ident: _,
|
||||
}
|
||||
} else {
|
||||
quote_spanned! {self.ident.span()=>
|
||||
#field_ident,
|
||||
}
|
||||
}
|
||||
}));
|
||||
quote_spanned! {self.ident.span()=>
|
||||
#[automatically_derived]
|
||||
#[allow(non_camel_case_types, non_snake_case, dead_code)]
|
||||
impl #impl_generics #unfilled_ty
|
||||
#where_clause
|
||||
{
|
||||
#vis fn #fn_ident(
|
||||
self,
|
||||
#field_ident: impl ::fayalite::expr::ToExpr<Type = #ty>,
|
||||
) -> #filled_ty {
|
||||
let Self {
|
||||
#phantom_field_name: _,
|
||||
#(#pat_fields)*
|
||||
} = self;
|
||||
let #field_ident = ::fayalite::expr::ToExpr::to_expr(&#field_ident);
|
||||
#ident {
|
||||
#phantom_field_name: ::fayalite::__std::marker::PhantomData,
|
||||
#(#field_idents,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
}
|
||||
let unfilled_generics = self.builder_struct_generics(|_| BuilderFieldState::Unfilled);
|
||||
let unfilled_ty = self.builder_struct_ty(|_| BuilderFieldState::Unfilled);
|
||||
let (unfilled_impl_generics, _, unfilled_where_clause) = unfilled_generics.split_for_impl();
|
||||
quote_spanned! {self.ident.span()=>
|
||||
#[automatically_derived]
|
||||
#[allow(non_camel_case_types, dead_code)]
|
||||
impl #unfilled_impl_generics ::fayalite::__std::default::Default for #unfilled_ty
|
||||
#unfilled_where_clause
|
||||
{
|
||||
fn default() -> Self {
|
||||
#ident {
|
||||
#phantom_field_name: ::fayalite::__std::marker::PhantomData,
|
||||
#(#field_idents: ::fayalite::__std::default::Default::default(),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
let filled_generics = self.builder_struct_generics(|_| BuilderFieldState::Filled);
|
||||
let filled_ty = self.builder_struct_ty(|_| BuilderFieldState::Filled);
|
||||
let (filled_impl_generics, _, filled_where_clause) = filled_generics.split_for_impl();
|
||||
let type_generics = self.generics.split_for_impl().1;
|
||||
quote_spanned! {self.ident.span()=>
|
||||
#[automatically_derived]
|
||||
#[allow(non_camel_case_types, dead_code)]
|
||||
impl #filled_impl_generics ::fayalite::expr::ToExpr for #filled_ty
|
||||
#filled_where_clause
|
||||
{
|
||||
type Type = #target #type_generics;
|
||||
fn to_expr(
|
||||
&self,
|
||||
) -> ::fayalite::expr::Expr<<Self as ::fayalite::expr::ToExpr>::Type> {
|
||||
let __ty = #target {
|
||||
#(#field_idents: ::fayalite::expr::Expr::ty(self.#field_idents),)*
|
||||
};
|
||||
let __field_values = [
|
||||
#(::fayalite::expr::Expr::canonical(self.#field_idents),)*
|
||||
];
|
||||
::fayalite::expr::ToExpr::to_expr(
|
||||
&::fayalite::expr::ops::BundleLiteral::new(
|
||||
__ty,
|
||||
::fayalite::intern::Intern::intern(&__field_values[..]),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for ParsedBundle {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self {
|
||||
attrs,
|
||||
options,
|
||||
vis,
|
||||
struct_token,
|
||||
ident,
|
||||
generics,
|
||||
fields,
|
||||
field_flips,
|
||||
mask_type_ident,
|
||||
mask_type_match_variant_ident,
|
||||
match_variant_ident,
|
||||
builder_ident,
|
||||
mask_type_builder_ident,
|
||||
} = self;
|
||||
let span = ident.span();
|
||||
let ItemOptions {
|
||||
outline_generated: _,
|
||||
target,
|
||||
custom_bounds: _,
|
||||
no_static,
|
||||
no_runtime_generics,
|
||||
} = &options.body;
|
||||
let target = get_target(target, ident);
|
||||
let mut item_attrs = attrs.clone();
|
||||
item_attrs.push(common_derives(span));
|
||||
ItemStruct {
|
||||
attrs: item_attrs,
|
||||
vis: vis.clone(),
|
||||
struct_token: *struct_token,
|
||||
ident: ident.clone(),
|
||||
generics: generics.into(),
|
||||
fields: Fields::Named(fields.clone().into()),
|
||||
semi_token: None,
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
|
||||
if let (MaybeParsed::Parsed(generics), MaybeParsed::Parsed(fields), None) =
|
||||
(generics, fields, no_runtime_generics)
|
||||
{
|
||||
generics.make_runtime_generics(tokens, vis, ident, &target, |context| {
|
||||
let fields: Vec<_> = fields
|
||||
.named
|
||||
.iter()
|
||||
.map(|ParsedField { ident, ty, .. }| {
|
||||
let ident = ident.as_ref().unwrap();
|
||||
let expr = ty.make_hdl_type_expr(context);
|
||||
quote_spanned! {span=>
|
||||
#ident: #expr,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
parse_quote_spanned! {span=>
|
||||
#target {
|
||||
#(#fields)*
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
let mut wrapped_in_const = WrappedInConst::new(tokens, span);
|
||||
let tokens = wrapped_in_const.inner();
|
||||
let builder = Builder {
|
||||
vis: vis.clone(),
|
||||
struct_token: *struct_token,
|
||||
ident: builder_ident.clone(),
|
||||
target: target.clone(),
|
||||
generics: generics.into(),
|
||||
fields: fields.clone().into(),
|
||||
};
|
||||
builder.to_tokens(tokens);
|
||||
let unfilled_builder_ty = builder.builder_struct_ty(|_| BuilderFieldState::Unfilled);
|
||||
let filled_builder_ty = builder.builder_struct_ty(|_| BuilderFieldState::Filled);
|
||||
let mut mask_type_fields = FieldsNamed::from(fields.clone());
|
||||
for Field { ty, .. } in &mut mask_type_fields.named {
|
||||
*ty = parse_quote_spanned! {span=>
|
||||
<#ty as ::fayalite::ty::Type>::MaskType
|
||||
};
|
||||
}
|
||||
let mask_type_builder = Builder {
|
||||
vis: vis.clone(),
|
||||
struct_token: *struct_token,
|
||||
ident: mask_type_builder_ident.clone(),
|
||||
target: mask_type_ident.clone().into(),
|
||||
generics: generics.into(),
|
||||
fields: mask_type_fields.clone(),
|
||||
};
|
||||
mask_type_builder.to_tokens(tokens);
|
||||
let unfilled_mask_type_builder_ty =
|
||||
mask_type_builder.builder_struct_ty(|_| BuilderFieldState::Unfilled);
|
||||
let filled_mask_type_builder_ty =
|
||||
mask_type_builder.builder_struct_ty(|_| BuilderFieldState::Filled);
|
||||
ItemStruct {
|
||||
attrs: vec![
|
||||
common_derives(span),
|
||||
parse_quote_spanned! {span=>
|
||||
#[allow(non_camel_case_types, dead_code)]
|
||||
},
|
||||
],
|
||||
vis: vis.clone(),
|
||||
struct_token: *struct_token,
|
||||
ident: mask_type_ident.clone(),
|
||||
generics: generics.into(),
|
||||
fields: Fields::Named(mask_type_fields.clone()),
|
||||
semi_token: None,
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
let mut mask_type_match_variant_fields = mask_type_fields;
|
||||
for Field { ty, .. } in &mut mask_type_match_variant_fields.named {
|
||||
*ty = parse_quote_spanned! {span=>
|
||||
::fayalite::expr::Expr<#ty>
|
||||
};
|
||||
}
|
||||
ItemStruct {
|
||||
attrs: vec![
|
||||
common_derives(span),
|
||||
parse_quote_spanned! {span=>
|
||||
#[allow(non_camel_case_types, dead_code)]
|
||||
},
|
||||
],
|
||||
vis: vis.clone(),
|
||||
struct_token: *struct_token,
|
||||
ident: mask_type_match_variant_ident.clone(),
|
||||
generics: generics.into(),
|
||||
fields: Fields::Named(mask_type_match_variant_fields),
|
||||
semi_token: None,
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
let mut match_variant_fields = FieldsNamed::from(fields.clone());
|
||||
for Field { ty, .. } in &mut match_variant_fields.named {
|
||||
*ty = parse_quote_spanned! {span=>
|
||||
::fayalite::expr::Expr<#ty>
|
||||
};
|
||||
}
|
||||
ItemStruct {
|
||||
attrs: vec![
|
||||
common_derives(span),
|
||||
parse_quote_spanned! {span=>
|
||||
#[allow(non_camel_case_types, dead_code)]
|
||||
},
|
||||
],
|
||||
vis: vis.clone(),
|
||||
struct_token: *struct_token,
|
||||
ident: match_variant_ident.clone(),
|
||||
generics: generics.into(),
|
||||
fields: Fields::Named(match_variant_fields),
|
||||
semi_token: None,
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
let this_token = Ident::new("__this", span);
|
||||
let fields_token = Ident::new("__fields", span);
|
||||
let self_token = Token![self](span);
|
||||
let match_variant_body_fields = Vec::from_iter(fields.named().into_iter().map(|field| {
|
||||
let ident: &Ident = field.ident().as_ref().unwrap();
|
||||
let ident_str = ident.to_string();
|
||||
quote_spanned! {span=>
|
||||
#ident: ::fayalite::expr::Expr::field(#this_token, #ident_str),
|
||||
}
|
||||
}));
|
||||
let mask_type_body_fields = Vec::from_iter(fields.named().into_iter().map(|field| {
|
||||
let ident: &Ident = field.ident().as_ref().unwrap();
|
||||
quote_spanned! {span=>
|
||||
#ident: ::fayalite::ty::Type::mask_type(&#self_token.#ident),
|
||||
}
|
||||
}));
|
||||
let from_canonical_body_fields =
|
||||
Vec::from_iter(fields.named().into_iter().enumerate().zip(field_flips).map(
|
||||
|((index, field), flip)| {
|
||||
let ident: &Ident = field.ident().as_ref().unwrap();
|
||||
let ident_str = ident.to_string();
|
||||
let not_flipped = flip.is_none().then(|| Token![!](span));
|
||||
quote_spanned! {span=>
|
||||
#ident: {
|
||||
let ::fayalite::bundle::BundleField {
|
||||
name: __name,
|
||||
flipped: __flipped,
|
||||
ty: __ty,
|
||||
} = #fields_token[#index];
|
||||
::fayalite::__std::assert_eq!(&*__name, #ident_str);
|
||||
::fayalite::__std::assert!(#not_flipped __flipped);
|
||||
::fayalite::ty::Type::from_canonical(__ty)
|
||||
},
|
||||
}
|
||||
},
|
||||
));
|
||||
let fields_body_fields = Vec::from_iter(fields.named().into_iter().zip(field_flips).map(
|
||||
|(field, flip)| {
|
||||
let ident: &Ident = field.ident().as_ref().unwrap();
|
||||
let ident_str = ident.to_string();
|
||||
let flipped = flip.is_some();
|
||||
quote_spanned! {span=>
|
||||
::fayalite::bundle::BundleField {
|
||||
name: ::fayalite::intern::Intern::intern(#ident_str),
|
||||
flipped: #flipped,
|
||||
ty: ::fayalite::ty::Type::canonical(&#self_token.#ident),
|
||||
},
|
||||
}
|
||||
},
|
||||
));
|
||||
let fields_len = fields.named().into_iter().len();
|
||||
quote_spanned! {span=>
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::ty::Type for #mask_type_ident #type_generics
|
||||
#where_clause
|
||||
{
|
||||
type BaseType = ::fayalite::bundle::Bundle;
|
||||
type MaskType = #mask_type_ident #type_generics;
|
||||
type MatchVariant = #mask_type_match_variant_ident #type_generics;
|
||||
type MatchActiveScope = ();
|
||||
type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope<
|
||||
<Self as ::fayalite::ty::Type>::MatchVariant,
|
||||
>;
|
||||
type MatchVariantsIter = ::fayalite::__std::iter::Once<
|
||||
<Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope,
|
||||
>;
|
||||
fn match_variants(
|
||||
#this_token: ::fayalite::expr::Expr<Self>,
|
||||
__source_location: ::fayalite::source_location::SourceLocation,
|
||||
) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter {
|
||||
let __retval = #mask_type_match_variant_ident {
|
||||
#(#match_variant_body_fields)*
|
||||
};
|
||||
::fayalite::__std::iter::once(::fayalite::ty::MatchVariantWithoutScope(__retval))
|
||||
}
|
||||
fn mask_type(&#self_token) -> <Self as ::fayalite::ty::Type>::MaskType {
|
||||
*#self_token
|
||||
}
|
||||
fn canonical(&#self_token) -> ::fayalite::ty::CanonicalType {
|
||||
::fayalite::ty::Type::canonical(&::fayalite::bundle::Bundle::new(::fayalite::bundle::BundleType::fields(#self_token)))
|
||||
}
|
||||
#[track_caller]
|
||||
fn from_canonical(__canonical_type: ::fayalite::ty::CanonicalType) -> Self {
|
||||
let ::fayalite::ty::CanonicalType::Bundle(__bundle) = __canonical_type else {
|
||||
::fayalite::__std::panic!("expected bundle");
|
||||
};
|
||||
let #fields_token = ::fayalite::bundle::BundleType::fields(&__bundle);
|
||||
::fayalite::__std::assert_eq!(#fields_token.len(), #fields_len, "bundle has wrong number of fields");
|
||||
Self {
|
||||
#(#from_canonical_body_fields)*
|
||||
}
|
||||
}
|
||||
fn source_location() -> ::fayalite::source_location::SourceLocation {
|
||||
::fayalite::source_location::SourceLocation::caller()
|
||||
}
|
||||
}
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::bundle::BundleType for #mask_type_ident #type_generics
|
||||
#where_clause
|
||||
{
|
||||
type Builder = #unfilled_mask_type_builder_ty;
|
||||
type FilledBuilder = #filled_mask_type_builder_ty;
|
||||
fn fields(&#self_token) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> {
|
||||
::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..])
|
||||
}
|
||||
}
|
||||
impl #impl_generics #mask_type_ident #type_generics
|
||||
#where_clause
|
||||
{
|
||||
#vis fn __bundle_builder() -> #unfilled_mask_type_builder_ty {
|
||||
::fayalite::__std::default::Default::default()
|
||||
}
|
||||
}
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::ty::TypeWithDeref for #mask_type_ident #type_generics
|
||||
#where_clause
|
||||
{
|
||||
fn expr_deref(#this_token: &::fayalite::expr::Expr<Self>) -> &<Self as ::fayalite::ty::Type>::MatchVariant {
|
||||
let #this_token = *#this_token;
|
||||
let __retval = #mask_type_match_variant_ident {
|
||||
#(#match_variant_body_fields)*
|
||||
};
|
||||
::fayalite::intern::Interned::into_inner(::fayalite::intern::Intern::intern_sized(__retval))
|
||||
}
|
||||
}
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::ty::Type for #target #type_generics
|
||||
#where_clause
|
||||
{
|
||||
type BaseType = ::fayalite::bundle::Bundle;
|
||||
type MaskType = #mask_type_ident #type_generics;
|
||||
type MatchVariant = #match_variant_ident #type_generics;
|
||||
type MatchActiveScope = ();
|
||||
type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope<
|
||||
<Self as ::fayalite::ty::Type>::MatchVariant,
|
||||
>;
|
||||
type MatchVariantsIter = ::fayalite::__std::iter::Once<
|
||||
<Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope,
|
||||
>;
|
||||
fn match_variants(
|
||||
#this_token: ::fayalite::expr::Expr<Self>,
|
||||
__source_location: ::fayalite::source_location::SourceLocation,
|
||||
) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter {
|
||||
let __retval = #match_variant_ident {
|
||||
#(#match_variant_body_fields)*
|
||||
};
|
||||
::fayalite::__std::iter::once(::fayalite::ty::MatchVariantWithoutScope(__retval))
|
||||
}
|
||||
fn mask_type(&#self_token) -> <Self as ::fayalite::ty::Type>::MaskType {
|
||||
#mask_type_ident {
|
||||
#(#mask_type_body_fields)*
|
||||
}
|
||||
}
|
||||
fn canonical(&#self_token) -> ::fayalite::ty::CanonicalType {
|
||||
::fayalite::ty::Type::canonical(&::fayalite::bundle::Bundle::new(::fayalite::bundle::BundleType::fields(#self_token)))
|
||||
}
|
||||
#[track_caller]
|
||||
fn from_canonical(__canonical_type: ::fayalite::ty::CanonicalType) -> Self {
|
||||
let ::fayalite::ty::CanonicalType::Bundle(__bundle) = __canonical_type else {
|
||||
::fayalite::__std::panic!("expected bundle");
|
||||
};
|
||||
let #fields_token = ::fayalite::bundle::BundleType::fields(&__bundle);
|
||||
::fayalite::__std::assert_eq!(#fields_token.len(), #fields_len, "bundle has wrong number of fields");
|
||||
Self {
|
||||
#(#from_canonical_body_fields)*
|
||||
}
|
||||
}
|
||||
fn source_location() -> ::fayalite::source_location::SourceLocation {
|
||||
::fayalite::source_location::SourceLocation::caller()
|
||||
}
|
||||
}
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::bundle::BundleType for #target #type_generics
|
||||
#where_clause
|
||||
{
|
||||
type Builder = #unfilled_builder_ty;
|
||||
type FilledBuilder = #filled_builder_ty;
|
||||
fn fields(&#self_token) -> ::fayalite::intern::Interned<[::fayalite::bundle::BundleField]> {
|
||||
::fayalite::intern::Intern::intern(&[#(#fields_body_fields)*][..])
|
||||
}
|
||||
}
|
||||
impl #impl_generics #target #type_generics
|
||||
#where_clause
|
||||
{
|
||||
#vis fn __bundle_builder() -> #unfilled_builder_ty {
|
||||
::fayalite::__std::default::Default::default()
|
||||
}
|
||||
}
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::ty::TypeWithDeref for #target #type_generics
|
||||
#where_clause
|
||||
{
|
||||
fn expr_deref(#this_token: &::fayalite::expr::Expr<Self>) -> &<Self as ::fayalite::ty::Type>::MatchVariant {
|
||||
let #this_token = *#this_token;
|
||||
let __retval = #match_variant_ident {
|
||||
#(#match_variant_body_fields)*
|
||||
};
|
||||
::fayalite::intern::Interned::into_inner(::fayalite::intern::Intern::intern_sized(__retval))
|
||||
}
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) {
|
||||
let static_generics = generics.clone().for_static_type();
|
||||
let (static_impl_generics, static_type_generics, static_where_clause) =
|
||||
static_generics.split_for_impl();
|
||||
let static_type_body_fields = Vec::from_iter(fields.named().into_iter().map(|field| {
|
||||
let ident: &Ident = field.ident().as_ref().unwrap();
|
||||
let ty = field.ty();
|
||||
quote_spanned! {span=>
|
||||
#ident: <#ty as ::fayalite::ty::StaticType>::TYPE,
|
||||
}
|
||||
}));
|
||||
let static_mask_type_body_fields =
|
||||
Vec::from_iter(fields.named().into_iter().map(|field| {
|
||||
let ident: &Ident = field.ident().as_ref().unwrap();
|
||||
let ty = field.ty();
|
||||
quote_spanned! {span=>
|
||||
#ident: <#ty as ::fayalite::ty::StaticType>::MASK_TYPE,
|
||||
}
|
||||
}));
|
||||
let type_properties = format_ident!("__type_properties", span = span);
|
||||
let type_properties_fields = Vec::from_iter(fields.named().into_iter().zip(field_flips).map(|(field, field_flip)| {
|
||||
let flipped = field_flip.is_some();
|
||||
let ty = field.ty();
|
||||
quote_spanned! {span=>
|
||||
let #type_properties = #type_properties.field(#flipped, <#ty as ::fayalite::ty::StaticType>::TYPE_PROPERTIES);
|
||||
}
|
||||
}));
|
||||
let type_properties_mask_fields = Vec::from_iter(fields.named().into_iter().zip(field_flips).map(|(field, field_flip)| {
|
||||
let flipped = field_flip.is_some();
|
||||
let ty = field.ty();
|
||||
quote_spanned! {span=>
|
||||
let #type_properties = #type_properties.field(#flipped, <#ty as ::fayalite::ty::StaticType>::MASK_TYPE_PROPERTIES);
|
||||
}
|
||||
}));
|
||||
quote_spanned! {span=>
|
||||
#[automatically_derived]
|
||||
impl #static_impl_generics ::fayalite::ty::StaticType for #mask_type_ident #static_type_generics
|
||||
#static_where_clause
|
||||
{
|
||||
const TYPE: Self = Self {
|
||||
#(#static_mask_type_body_fields)*
|
||||
};
|
||||
const MASK_TYPE: <Self as ::fayalite::ty::Type>::MaskType = Self {
|
||||
#(#static_mask_type_body_fields)*
|
||||
};
|
||||
const TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = {
|
||||
let #type_properties = ::fayalite::bundle::BundleTypePropertiesBuilder::new();
|
||||
#(#type_properties_mask_fields)*
|
||||
#type_properties.finish()
|
||||
};
|
||||
const MASK_TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = {
|
||||
let #type_properties = ::fayalite::bundle::BundleTypePropertiesBuilder::new();
|
||||
#(#type_properties_mask_fields)*
|
||||
#type_properties.finish()
|
||||
};
|
||||
}
|
||||
#[automatically_derived]
|
||||
impl #static_impl_generics ::fayalite::ty::StaticType for #target #static_type_generics
|
||||
#static_where_clause
|
||||
{
|
||||
const TYPE: Self = Self {
|
||||
#(#static_type_body_fields)*
|
||||
};
|
||||
const MASK_TYPE: <Self as ::fayalite::ty::Type>::MaskType = #mask_type_ident {
|
||||
#(#static_mask_type_body_fields)*
|
||||
};
|
||||
const TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = {
|
||||
let #type_properties = ::fayalite::bundle::BundleTypePropertiesBuilder::new();
|
||||
#(#type_properties_fields)*
|
||||
#type_properties.finish()
|
||||
};
|
||||
const MASK_TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = {
|
||||
let #type_properties = ::fayalite::bundle::BundleTypePropertiesBuilder::new();
|
||||
#(#type_properties_mask_fields)*
|
||||
#type_properties.finish()
|
||||
};
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn hdl_bundle(item: ItemStruct) -> syn::Result<TokenStream> {
|
||||
let item = ParsedBundle::parse(item)?;
|
||||
let outline_generated = item.options.body.outline_generated;
|
||||
let mut contents = item.to_token_stream();
|
||||
if outline_generated.is_some() {
|
||||
contents = crate::outline_generated(contents, "hdl-bundle-");
|
||||
}
|
||||
Ok(contents)
|
||||
}
|
|
@ -1,664 +0,0 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
hdl_type_common::{
|
||||
common_derives, get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics,
|
||||
ParsedType, SplitForImpl, TypesParser, WrappedInConst,
|
||||
},
|
||||
kw, Errors, HdlAttr, PairsIterExt,
|
||||
};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote_spanned, ToTokens};
|
||||
use syn::{
|
||||
parse_quote_spanned,
|
||||
punctuated::{Pair, Punctuated},
|
||||
token::{Brace, Paren},
|
||||
Attribute, Field, FieldMutability, Fields, FieldsNamed, FieldsUnnamed, Generics, Ident,
|
||||
ItemEnum, ItemStruct, Token, Type, Variant, Visibility,
|
||||
};
|
||||
|
||||
crate::options! {
|
||||
#[options = VariantOptions]
|
||||
pub(crate) enum VariantOption {}
|
||||
}
|
||||
|
||||
crate::options! {
|
||||
#[options = FieldOptions]
|
||||
pub(crate) enum FieldOption {}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct ParsedVariantField {
|
||||
pub(crate) paren_token: Paren,
|
||||
pub(crate) attrs: Vec<Attribute>,
|
||||
pub(crate) options: HdlAttr<FieldOptions, kw::hdl>,
|
||||
pub(crate) ty: MaybeParsed<ParsedType, Type>,
|
||||
pub(crate) comma_token: Option<Token![,]>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct ParsedVariant {
|
||||
pub(crate) attrs: Vec<Attribute>,
|
||||
pub(crate) options: HdlAttr<VariantOptions, kw::hdl>,
|
||||
pub(crate) ident: Ident,
|
||||
pub(crate) field: Option<ParsedVariantField>,
|
||||
}
|
||||
|
||||
impl ParsedVariant {
|
||||
fn parse(
|
||||
errors: &mut Errors,
|
||||
variant: Variant,
|
||||
generics: &MaybeParsed<ParsedGenerics, Generics>,
|
||||
) -> Self {
|
||||
let Variant {
|
||||
mut attrs,
|
||||
ident,
|
||||
fields,
|
||||
discriminant,
|
||||
} = variant;
|
||||
let options = errors
|
||||
.unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs))
|
||||
.unwrap_or_default();
|
||||
let field = match fields {
|
||||
Fields::Unnamed(FieldsUnnamed {
|
||||
paren_token,
|
||||
unnamed,
|
||||
}) if unnamed.len() == 1 => {
|
||||
let (field, comma_token) = unnamed.into_pairs().next().unwrap().into_tuple();
|
||||
let Field {
|
||||
mut attrs,
|
||||
vis,
|
||||
mutability,
|
||||
ident: _,
|
||||
colon_token: _,
|
||||
ty,
|
||||
} = field;
|
||||
let options = errors
|
||||
.unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs))
|
||||
.unwrap_or_default();
|
||||
if !matches!(vis, Visibility::Inherited) {
|
||||
errors.error(
|
||||
&vis,
|
||||
"enum variant fields must not have a visibility specifier",
|
||||
);
|
||||
}
|
||||
if !matches!(mutability, FieldMutability::None) {
|
||||
// FIXME: use mutability as the spanned tokens,
|
||||
// blocked on https://github.com/dtolnay/syn/issues/1717
|
||||
errors.error(&ty, "field mutability is not supported");
|
||||
}
|
||||
Some(ParsedVariantField {
|
||||
paren_token,
|
||||
attrs,
|
||||
options,
|
||||
ty: TypesParser::maybe_run(generics.as_ref(), ty, errors),
|
||||
comma_token,
|
||||
})
|
||||
}
|
||||
Fields::Unit => None,
|
||||
Fields::Unnamed(fields) if fields.unnamed.is_empty() => None,
|
||||
Fields::Named(fields) if fields.named.is_empty() => None,
|
||||
Fields::Unnamed(_) | Fields::Named(_) => {
|
||||
errors.error(
|
||||
fields,
|
||||
"enum variant must either have no fields or a single parenthesized field",
|
||||
);
|
||||
None
|
||||
}
|
||||
};
|
||||
if let Some((eq, _)) = discriminant {
|
||||
errors.error(eq, "custom enum discriminants are not allowed");
|
||||
}
|
||||
Self {
|
||||
attrs,
|
||||
options,
|
||||
ident,
|
||||
field,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct ParsedEnum {
|
||||
pub(crate) attrs: Vec<Attribute>,
|
||||
pub(crate) options: HdlAttr<ItemOptions, kw::hdl>,
|
||||
pub(crate) vis: Visibility,
|
||||
pub(crate) enum_token: Token![enum],
|
||||
pub(crate) ident: Ident,
|
||||
pub(crate) generics: MaybeParsed<ParsedGenerics, Generics>,
|
||||
pub(crate) brace_token: Brace,
|
||||
pub(crate) variants: Punctuated<ParsedVariant, Token![,]>,
|
||||
pub(crate) match_variant_ident: Ident,
|
||||
}
|
||||
|
||||
impl ParsedEnum {
|
||||
fn parse(item: ItemEnum) -> syn::Result<Self> {
|
||||
let ItemEnum {
|
||||
mut attrs,
|
||||
vis,
|
||||
enum_token,
|
||||
ident,
|
||||
mut generics,
|
||||
brace_token,
|
||||
variants,
|
||||
} = item;
|
||||
let mut errors = Errors::new();
|
||||
let mut options = errors
|
||||
.unwrap_or_default(HdlAttr::<ItemOptions, kw::hdl>::parse_and_take_attr(
|
||||
&mut attrs,
|
||||
))
|
||||
.unwrap_or_default();
|
||||
errors.ok(options.body.validate());
|
||||
let ItemOptions {
|
||||
outline_generated: _,
|
||||
target: _,
|
||||
custom_bounds,
|
||||
no_static: _,
|
||||
no_runtime_generics: _,
|
||||
} = options.body;
|
||||
attrs.retain(|attr| {
|
||||
if attr.path().is_ident("repr") {
|
||||
errors.error(attr, "#[repr] is not supported on #[hdl] enums");
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
let generics = if custom_bounds.is_some() {
|
||||
MaybeParsed::Unrecognized(generics)
|
||||
} else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) {
|
||||
MaybeParsed::Parsed(generics)
|
||||
} else {
|
||||
MaybeParsed::Unrecognized(generics)
|
||||
};
|
||||
let variants = Punctuated::from_iter(
|
||||
variants
|
||||
.into_pairs()
|
||||
.map_pair_value(|v| ParsedVariant::parse(&mut errors, v, &generics)),
|
||||
);
|
||||
errors.finish()?;
|
||||
Ok(Self {
|
||||
attrs,
|
||||
options,
|
||||
vis,
|
||||
enum_token,
|
||||
generics,
|
||||
brace_token,
|
||||
variants,
|
||||
match_variant_ident: format_ident!("__{}__MatchVariant", ident),
|
||||
ident,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for ParsedEnum {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self {
|
||||
attrs,
|
||||
options,
|
||||
vis,
|
||||
enum_token,
|
||||
ident,
|
||||
generics,
|
||||
brace_token,
|
||||
variants,
|
||||
match_variant_ident,
|
||||
} = self;
|
||||
let span = ident.span();
|
||||
let ItemOptions {
|
||||
outline_generated: _,
|
||||
target,
|
||||
custom_bounds: _,
|
||||
no_static,
|
||||
no_runtime_generics,
|
||||
} = &options.body;
|
||||
let target = get_target(target, ident);
|
||||
let mut struct_attrs = attrs.clone();
|
||||
struct_attrs.push(common_derives(span));
|
||||
struct_attrs.push(parse_quote_spanned! {span=>
|
||||
#[allow(non_snake_case)]
|
||||
});
|
||||
let struct_fields = Punctuated::from_iter(variants.pairs().map_pair_value_ref(
|
||||
|ParsedVariant {
|
||||
attrs: _,
|
||||
options,
|
||||
ident,
|
||||
field,
|
||||
}| {
|
||||
let VariantOptions {} = options.body;
|
||||
let colon_token;
|
||||
let ty = if let Some(ParsedVariantField {
|
||||
paren_token,
|
||||
attrs: _,
|
||||
options,
|
||||
ty,
|
||||
comma_token: _,
|
||||
}) = field
|
||||
{
|
||||
let FieldOptions {} = options.body;
|
||||
colon_token = Token![:](paren_token.span.open());
|
||||
ty.clone().into()
|
||||
} else {
|
||||
colon_token = Token![:](span);
|
||||
parse_quote_spanned! {span=>
|
||||
()
|
||||
}
|
||||
};
|
||||
Field {
|
||||
attrs: vec![],
|
||||
vis: vis.clone(),
|
||||
mutability: FieldMutability::None,
|
||||
ident: Some(ident.clone()),
|
||||
colon_token: Some(colon_token),
|
||||
ty,
|
||||
}
|
||||
},
|
||||
));
|
||||
ItemStruct {
|
||||
attrs: struct_attrs,
|
||||
vis: vis.clone(),
|
||||
struct_token: Token![struct](enum_token.span),
|
||||
ident: ident.clone(),
|
||||
generics: generics.into(),
|
||||
fields: if struct_fields.is_empty() {
|
||||
Fields::Unit
|
||||
} else {
|
||||
Fields::Named(FieldsNamed {
|
||||
brace_token: *brace_token,
|
||||
named: struct_fields,
|
||||
})
|
||||
},
|
||||
semi_token: None,
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
|
||||
if let (MaybeParsed::Parsed(generics), None) = (generics, no_runtime_generics) {
|
||||
generics.make_runtime_generics(tokens, vis, ident, &target, |context| {
|
||||
let fields: Vec<_> = variants
|
||||
.iter()
|
||||
.map(|ParsedVariant { ident, field, .. }| {
|
||||
if let Some(ParsedVariantField {
|
||||
ty: MaybeParsed::Parsed(ty),
|
||||
..
|
||||
}) = field
|
||||
{
|
||||
let expr = ty.make_hdl_type_expr(context);
|
||||
quote_spanned! {span=>
|
||||
#ident: #expr,
|
||||
}
|
||||
} else {
|
||||
quote_spanned! {span=>
|
||||
#ident: (),
|
||||
}
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
parse_quote_spanned! {span=>
|
||||
#target {
|
||||
#(#fields)*
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
let mut wrapped_in_const = WrappedInConst::new(tokens, span);
|
||||
let tokens = wrapped_in_const.inner();
|
||||
{
|
||||
let mut wrapped_in_const = WrappedInConst::new(tokens, span);
|
||||
let tokens = wrapped_in_const.inner();
|
||||
let mut enum_attrs = attrs.clone();
|
||||
enum_attrs.push(parse_quote_spanned! {span=>
|
||||
#[allow(dead_code)]
|
||||
});
|
||||
ItemEnum {
|
||||
attrs: enum_attrs,
|
||||
vis: vis.clone(),
|
||||
enum_token: *enum_token,
|
||||
ident: ident.clone(),
|
||||
generics: generics.into(),
|
||||
brace_token: *brace_token,
|
||||
variants: Punctuated::from_iter(variants.pairs().map_pair_value_ref(
|
||||
|ParsedVariant {
|
||||
attrs,
|
||||
options: _,
|
||||
ident,
|
||||
field,
|
||||
}| Variant {
|
||||
attrs: attrs.clone(),
|
||||
ident: ident.clone(),
|
||||
fields: match field {
|
||||
Some(ParsedVariantField {
|
||||
paren_token,
|
||||
attrs,
|
||||
options: _,
|
||||
ty,
|
||||
comma_token,
|
||||
}) => Fields::Unnamed(FieldsUnnamed {
|
||||
paren_token: *paren_token,
|
||||
unnamed: Punctuated::from_iter([Pair::new(
|
||||
Field {
|
||||
attrs: attrs.clone(),
|
||||
vis: Visibility::Inherited,
|
||||
mutability: FieldMutability::None,
|
||||
ident: None,
|
||||
colon_token: None,
|
||||
ty: ty.clone().into(),
|
||||
},
|
||||
*comma_token,
|
||||
)]),
|
||||
}),
|
||||
None => Fields::Unit,
|
||||
},
|
||||
discriminant: None,
|
||||
},
|
||||
)),
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
}
|
||||
let mut enum_attrs = attrs.clone();
|
||||
enum_attrs.push(parse_quote_spanned! {span=>
|
||||
#[allow(dead_code, non_camel_case_types)]
|
||||
});
|
||||
ItemEnum {
|
||||
attrs: enum_attrs,
|
||||
vis: vis.clone(),
|
||||
enum_token: *enum_token,
|
||||
ident: match_variant_ident.clone(),
|
||||
generics: generics.into(),
|
||||
brace_token: *brace_token,
|
||||
variants: Punctuated::from_iter(variants.pairs().map_pair_value_ref(
|
||||
|ParsedVariant {
|
||||
attrs,
|
||||
options: _,
|
||||
ident,
|
||||
field,
|
||||
}| Variant {
|
||||
attrs: attrs.clone(),
|
||||
ident: ident.clone(),
|
||||
fields: match field {
|
||||
Some(ParsedVariantField {
|
||||
paren_token,
|
||||
attrs,
|
||||
options: _,
|
||||
ty,
|
||||
comma_token,
|
||||
}) => Fields::Unnamed(FieldsUnnamed {
|
||||
paren_token: *paren_token,
|
||||
unnamed: Punctuated::from_iter([Pair::new(
|
||||
Field {
|
||||
attrs: attrs.clone(),
|
||||
vis: Visibility::Inherited,
|
||||
mutability: FieldMutability::None,
|
||||
ident: None,
|
||||
colon_token: None,
|
||||
ty: parse_quote_spanned! {span=>
|
||||
::fayalite::expr::Expr<#ty>
|
||||
},
|
||||
},
|
||||
*comma_token,
|
||||
)]),
|
||||
}),
|
||||
None => Fields::Unit,
|
||||
},
|
||||
discriminant: None,
|
||||
},
|
||||
)),
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
let self_token = Token![self](span);
|
||||
for (index, ParsedVariant { ident, field, .. }) in variants.iter().enumerate() {
|
||||
if let Some(ParsedVariantField { ty, .. }) = field {
|
||||
quote_spanned! {span=>
|
||||
#[automatically_derived]
|
||||
impl #impl_generics #target #type_generics
|
||||
#where_clause
|
||||
{
|
||||
#[allow(non_snake_case, dead_code)]
|
||||
#vis fn #ident<__V: ::fayalite::expr::ToExpr<Type = #ty>>(
|
||||
#self_token,
|
||||
v: __V,
|
||||
) -> ::fayalite::expr::Expr<Self> {
|
||||
::fayalite::expr::ToExpr::to_expr(
|
||||
&::fayalite::expr::ops::EnumLiteral::new_by_index(
|
||||
#self_token,
|
||||
#index,
|
||||
::fayalite::__std::option::Option::Some(
|
||||
::fayalite::expr::Expr::canonical(
|
||||
::fayalite::expr::ToExpr::to_expr(&v),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
quote_spanned! {span=>
|
||||
#[automatically_derived]
|
||||
impl #impl_generics #target #type_generics
|
||||
#where_clause
|
||||
{
|
||||
#[allow(non_snake_case, dead_code)]
|
||||
#vis fn #ident(#self_token) -> ::fayalite::expr::Expr<Self> {
|
||||
::fayalite::expr::ToExpr::to_expr(
|
||||
&::fayalite::expr::ops::EnumLiteral::new_by_index(
|
||||
#self_token,
|
||||
#index,
|
||||
::fayalite::__std::option::Option::None,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
}
|
||||
let variants_token = Ident::new("variants", span);
|
||||
let from_canonical_body_fields = Vec::from_iter(variants.iter().enumerate().map(
|
||||
|(index, ParsedVariant { ident, field, .. })| {
|
||||
let ident_str = ident.to_string();
|
||||
let val = if field.is_some() {
|
||||
let missing_value_msg = format!("expected variant {ident} to have a field");
|
||||
quote_spanned! {span=>
|
||||
::fayalite::ty::Type::from_canonical(ty.expect(#missing_value_msg))
|
||||
}
|
||||
} else {
|
||||
quote_spanned! {span=>
|
||||
::fayalite::__std::assert!(ty.is_none());
|
||||
}
|
||||
};
|
||||
quote_spanned! {span=>
|
||||
#ident: {
|
||||
let ::fayalite::enum_::EnumVariant {
|
||||
name,
|
||||
ty,
|
||||
} = #variants_token[#index];
|
||||
::fayalite::__std::assert_eq!(&*name, #ident_str);
|
||||
#val
|
||||
},
|
||||
}
|
||||
},
|
||||
));
|
||||
let variant_access_token = Ident::new("variant_access", span);
|
||||
let match_active_scope_match_arms = Vec::from_iter(variants.iter().enumerate().map(
|
||||
|(index, ParsedVariant { ident, field, .. })| {
|
||||
if field.is_some() {
|
||||
quote_spanned! {span=>
|
||||
#index => #match_variant_ident::#ident(
|
||||
::fayalite::expr::ToExpr::to_expr(
|
||||
&::fayalite::expr::ops::VariantAccess::new_by_index(
|
||||
#variant_access_token.base(),
|
||||
#variant_access_token.variant_index(),
|
||||
),
|
||||
),
|
||||
),
|
||||
}
|
||||
} else {
|
||||
quote_spanned! {span=>
|
||||
#index => #match_variant_ident::#ident,
|
||||
}
|
||||
}
|
||||
},
|
||||
));
|
||||
let variants_body_variants = Vec::from_iter(variants.iter().map(
|
||||
|ParsedVariant {
|
||||
attrs: _,
|
||||
options,
|
||||
ident,
|
||||
field,
|
||||
}| {
|
||||
let VariantOptions {} = options.body;
|
||||
let ident_str = ident.to_string();
|
||||
match field {
|
||||
Some(ParsedVariantField { options, .. }) => {
|
||||
let FieldOptions {} = options.body;
|
||||
quote_spanned! {span=>
|
||||
::fayalite::enum_::EnumVariant {
|
||||
name: ::fayalite::intern::Intern::intern(#ident_str),
|
||||
ty: ::fayalite::__std::option::Option::Some(
|
||||
::fayalite::ty::Type::canonical(&#self_token.#ident),
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
None => quote_spanned! {span=>
|
||||
::fayalite::enum_::EnumVariant {
|
||||
name: ::fayalite::intern::Intern::intern(#ident_str),
|
||||
ty: ::fayalite::__std::option::Option::None,
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
));
|
||||
let variants_len = variants.len();
|
||||
quote_spanned! {span=>
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::ty::Type for #target #type_generics
|
||||
#where_clause
|
||||
{
|
||||
type BaseType = ::fayalite::enum_::Enum;
|
||||
type MaskType = ::fayalite::int::Bool;
|
||||
type MatchVariant = #match_variant_ident #type_generics;
|
||||
type MatchActiveScope = ::fayalite::module::Scope;
|
||||
type MatchVariantAndInactiveScope = ::fayalite::enum_::EnumMatchVariantAndInactiveScope<Self>;
|
||||
type MatchVariantsIter = ::fayalite::enum_::EnumMatchVariantsIter<Self>;
|
||||
|
||||
fn match_variants(
|
||||
this: ::fayalite::expr::Expr<Self>,
|
||||
source_location: ::fayalite::source_location::SourceLocation,
|
||||
) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter {
|
||||
::fayalite::module::enum_match_variants_helper(this, source_location)
|
||||
}
|
||||
fn mask_type(&#self_token) -> <Self as ::fayalite::ty::Type>::MaskType {
|
||||
::fayalite::int::Bool
|
||||
}
|
||||
fn canonical(&#self_token) -> ::fayalite::ty::CanonicalType {
|
||||
::fayalite::ty::CanonicalType::Enum(::fayalite::enum_::Enum::new(::fayalite::enum_::EnumType::variants(#self_token)))
|
||||
}
|
||||
#[track_caller]
|
||||
#[allow(non_snake_case)]
|
||||
fn from_canonical(canonical_type: ::fayalite::ty::CanonicalType) -> Self {
|
||||
let ::fayalite::ty::CanonicalType::Enum(enum_) = canonical_type else {
|
||||
::fayalite::__std::panic!("expected enum");
|
||||
};
|
||||
let #variants_token = ::fayalite::enum_::EnumType::variants(&enum_);
|
||||
::fayalite::__std::assert_eq!(#variants_token.len(), #variants_len, "enum has wrong number of variants");
|
||||
Self {
|
||||
#(#from_canonical_body_fields)*
|
||||
}
|
||||
}
|
||||
fn source_location() -> ::fayalite::source_location::SourceLocation {
|
||||
::fayalite::source_location::SourceLocation::caller()
|
||||
}
|
||||
}
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::enum_::EnumType for #target #type_generics
|
||||
#where_clause
|
||||
{
|
||||
fn match_activate_scope(
|
||||
v: <Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope,
|
||||
) -> (<Self as ::fayalite::ty::Type>::MatchVariant, <Self as ::fayalite::ty::Type>::MatchActiveScope) {
|
||||
let (#variant_access_token, scope) = v.activate();
|
||||
(
|
||||
match #variant_access_token.variant_index() {
|
||||
#(#match_active_scope_match_arms)*
|
||||
#variants_len.. => ::fayalite::__std::panic!("invalid variant index"),
|
||||
},
|
||||
scope,
|
||||
)
|
||||
}
|
||||
fn variants(&#self_token) -> ::fayalite::intern::Interned<[::fayalite::enum_::EnumVariant]> {
|
||||
::fayalite::intern::Intern::intern(&[
|
||||
#(#variants_body_variants)*
|
||||
][..])
|
||||
}
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
if let (None, MaybeParsed::Parsed(generics)) = (no_static, &self.generics) {
|
||||
let static_generics = generics.clone().for_static_type();
|
||||
let (static_impl_generics, static_type_generics, static_where_clause) =
|
||||
static_generics.split_for_impl();
|
||||
let static_type_body_variants =
|
||||
Vec::from_iter(variants.iter().map(|ParsedVariant { ident, field, .. }| {
|
||||
if field.is_some() {
|
||||
quote_spanned! {span=>
|
||||
#ident: ::fayalite::ty::StaticType::TYPE,
|
||||
}
|
||||
} else {
|
||||
quote_spanned! {span=>
|
||||
#ident: (),
|
||||
}
|
||||
}
|
||||
}));
|
||||
let type_properties = format_ident!("__type_properties", span = span);
|
||||
let type_properties_variants =
|
||||
Vec::from_iter(variants.iter().map(|ParsedVariant { field, .. }| {
|
||||
let variant = if let Some(ParsedVariantField { ty, .. }) = field {
|
||||
quote_spanned! {span=>
|
||||
::fayalite::__std::option::Option::Some(
|
||||
<#ty as ::fayalite::ty::StaticType>::TYPE_PROPERTIES,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
quote_spanned! {span=>
|
||||
::fayalite::__std::option::Option::None
|
||||
}
|
||||
};
|
||||
quote_spanned! {span=>
|
||||
let #type_properties = #type_properties.variant(#variant);
|
||||
}
|
||||
}));
|
||||
quote_spanned! {span=>
|
||||
#[automatically_derived]
|
||||
impl #static_impl_generics ::fayalite::ty::StaticType
|
||||
for #target #static_type_generics
|
||||
#static_where_clause
|
||||
{
|
||||
const TYPE: Self = Self {
|
||||
#(#static_type_body_variants)*
|
||||
};
|
||||
const MASK_TYPE: <Self as ::fayalite::ty::Type>::MaskType =
|
||||
::fayalite::int::Bool;
|
||||
const TYPE_PROPERTIES: ::fayalite::ty::TypeProperties = {
|
||||
let #type_properties = ::fayalite::enum_::EnumTypePropertiesBuilder::new();
|
||||
#(#type_properties_variants)*
|
||||
#type_properties.finish()
|
||||
};
|
||||
const MASK_TYPE_PROPERTIES: ::fayalite::ty::TypeProperties =
|
||||
<::fayalite::int::Bool as ::fayalite::ty::StaticType>::TYPE_PROPERTIES;
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn hdl_enum(item: ItemEnum) -> syn::Result<TokenStream> {
|
||||
let item = ParsedEnum::parse(item)?;
|
||||
let outline_generated = item.options.body.outline_generated;
|
||||
let mut contents = item.to_token_stream();
|
||||
if outline_generated.is_some() {
|
||||
contents = crate::outline_generated(contents, "hdl-enum-");
|
||||
}
|
||||
Ok(contents)
|
||||
}
|
|
@ -1,133 +0,0 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
hdl_type_common::{
|
||||
get_target, ItemOptions, MakeHdlTypeExpr, MaybeParsed, ParsedGenerics, ParsedType,
|
||||
TypesParser,
|
||||
},
|
||||
kw, Errors, HdlAttr,
|
||||
};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::ToTokens;
|
||||
use syn::{parse_quote_spanned, Attribute, Generics, Ident, ItemType, Token, Type, Visibility};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct ParsedTypeAlias {
|
||||
pub(crate) attrs: Vec<Attribute>,
|
||||
pub(crate) options: HdlAttr<ItemOptions, kw::hdl>,
|
||||
pub(crate) vis: Visibility,
|
||||
pub(crate) type_token: Token![type],
|
||||
pub(crate) ident: Ident,
|
||||
pub(crate) generics: MaybeParsed<ParsedGenerics, Generics>,
|
||||
pub(crate) eq_token: Token![=],
|
||||
pub(crate) ty: MaybeParsed<ParsedType, Type>,
|
||||
pub(crate) semi_token: Token![;],
|
||||
}
|
||||
|
||||
impl ParsedTypeAlias {
|
||||
fn parse(item: ItemType) -> syn::Result<Self> {
|
||||
let ItemType {
|
||||
mut attrs,
|
||||
vis,
|
||||
type_token,
|
||||
ident,
|
||||
mut generics,
|
||||
eq_token,
|
||||
ty,
|
||||
semi_token,
|
||||
} = item;
|
||||
let mut errors = Errors::new();
|
||||
let mut options = errors
|
||||
.unwrap_or_default(HdlAttr::<ItemOptions, kw::hdl>::parse_and_take_attr(
|
||||
&mut attrs,
|
||||
))
|
||||
.unwrap_or_default();
|
||||
errors.ok(options.body.validate());
|
||||
let ItemOptions {
|
||||
outline_generated: _,
|
||||
target: _,
|
||||
custom_bounds,
|
||||
no_static,
|
||||
no_runtime_generics: _,
|
||||
} = options.body;
|
||||
if let Some((no_static,)) = no_static {
|
||||
errors.error(no_static, "no_static is not valid on type aliases");
|
||||
}
|
||||
let generics = if custom_bounds.is_some() {
|
||||
MaybeParsed::Unrecognized(generics)
|
||||
} else if let Some(generics) = errors.ok(ParsedGenerics::parse(&mut generics)) {
|
||||
MaybeParsed::Parsed(generics)
|
||||
} else {
|
||||
MaybeParsed::Unrecognized(generics)
|
||||
};
|
||||
let ty = TypesParser::maybe_run(generics.as_ref(), *ty, &mut errors);
|
||||
errors.finish()?;
|
||||
Ok(Self {
|
||||
attrs,
|
||||
options,
|
||||
vis,
|
||||
type_token,
|
||||
ident,
|
||||
generics,
|
||||
eq_token,
|
||||
ty,
|
||||
semi_token,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for ParsedTypeAlias {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self {
|
||||
attrs,
|
||||
options,
|
||||
vis,
|
||||
type_token,
|
||||
ident,
|
||||
generics,
|
||||
eq_token,
|
||||
ty,
|
||||
semi_token,
|
||||
} = self;
|
||||
let ItemOptions {
|
||||
outline_generated: _,
|
||||
target,
|
||||
custom_bounds: _,
|
||||
no_static: _,
|
||||
no_runtime_generics,
|
||||
} = &options.body;
|
||||
let target = get_target(target, ident);
|
||||
let mut type_attrs = attrs.clone();
|
||||
type_attrs.push(parse_quote_spanned! {ident.span()=>
|
||||
#[allow(type_alias_bounds)]
|
||||
});
|
||||
ItemType {
|
||||
attrs: type_attrs,
|
||||
vis: vis.clone(),
|
||||
type_token: *type_token,
|
||||
ident: ident.clone(),
|
||||
generics: generics.into(),
|
||||
eq_token: *eq_token,
|
||||
ty: Box::new(ty.clone().into()),
|
||||
semi_token: *semi_token,
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
if let (MaybeParsed::Parsed(generics), MaybeParsed::Parsed(ty), None) =
|
||||
(generics, ty, no_runtime_generics)
|
||||
{
|
||||
generics.make_runtime_generics(tokens, vis, ident, &target, |context| {
|
||||
ty.make_hdl_type_expr(context)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn hdl_type_alias_impl(item: ItemType) -> syn::Result<TokenStream> {
|
||||
let item = ParsedTypeAlias::parse(item)?;
|
||||
let outline_generated = item.options.body.outline_generated;
|
||||
let mut contents = item.to_token_stream();
|
||||
if outline_generated.is_some() {
|
||||
contents = crate::outline_generated(contents, "hdl-type-alias-");
|
||||
}
|
||||
Ok(contents)
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -9,33 +9,19 @@ use syn::{
|
|||
parse::{Parse, ParseStream, Parser},
|
||||
parse_quote,
|
||||
punctuated::Pair,
|
||||
spanned::Spanned,
|
||||
AttrStyle, Attribute, Error, Item, ItemFn, Token,
|
||||
AttrStyle, Attribute, Error, Item, Token,
|
||||
};
|
||||
|
||||
mod fold;
|
||||
mod hdl_bundle;
|
||||
mod hdl_enum;
|
||||
mod hdl_type_alias;
|
||||
mod hdl_type_common;
|
||||
mod module;
|
||||
|
||||
pub(crate) trait CustomToken:
|
||||
Copy
|
||||
+ Spanned
|
||||
+ ToTokens
|
||||
+ std::fmt::Debug
|
||||
+ Eq
|
||||
+ std::hash::Hash
|
||||
+ Default
|
||||
+ quote::IdentFragment
|
||||
+ Parse
|
||||
{
|
||||
const IDENT_STR: &'static str;
|
||||
}
|
||||
mod value_derive_common;
|
||||
mod value_derive_enum;
|
||||
mod value_derive_struct;
|
||||
|
||||
mod kw {
|
||||
pub(crate) use syn::token::Extern as extern_;
|
||||
pub(crate) use syn::token::{
|
||||
Enum as enum_, Extern as extern_, Static as static_, Struct as struct_, Where as where_,
|
||||
};
|
||||
|
||||
macro_rules! custom_keyword {
|
||||
($kw:ident) => {
|
||||
|
@ -52,33 +38,25 @@ mod kw {
|
|||
}
|
||||
|
||||
crate::fold::no_op_fold!($kw);
|
||||
|
||||
impl crate::CustomToken for $kw {
|
||||
const IDENT_STR: &'static str = stringify!($kw);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
custom_keyword!(clock_domain);
|
||||
custom_keyword!(connect_inexact);
|
||||
custom_keyword!(custom_bounds);
|
||||
custom_keyword!(flip);
|
||||
custom_keyword!(hdl);
|
||||
custom_keyword!(hdl_module);
|
||||
custom_keyword!(input);
|
||||
custom_keyword!(incomplete_wire);
|
||||
custom_keyword!(instance);
|
||||
custom_keyword!(m);
|
||||
custom_keyword!(memory);
|
||||
custom_keyword!(memory_array);
|
||||
custom_keyword!(memory_with_init);
|
||||
custom_keyword!(no_reset);
|
||||
custom_keyword!(no_runtime_generics);
|
||||
custom_keyword!(no_static);
|
||||
custom_keyword!(outline_generated);
|
||||
custom_keyword!(output);
|
||||
custom_keyword!(reg_builder);
|
||||
custom_keyword!(reset);
|
||||
custom_keyword!(reset_default);
|
||||
custom_keyword!(skip);
|
||||
custom_keyword!(target);
|
||||
custom_keyword!(wire);
|
||||
|
@ -87,34 +65,34 @@ mod kw {
|
|||
type Pound = Token![#]; // work around https://github.com/rust-lang/rust/issues/50676
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct HdlAttr<T, KW> {
|
||||
pub(crate) struct HdlAttr<T> {
|
||||
pub(crate) pound_token: Pound,
|
||||
pub(crate) style: AttrStyle,
|
||||
pub(crate) bracket_token: syn::token::Bracket,
|
||||
pub(crate) kw: KW,
|
||||
pub(crate) hdl: kw::hdl,
|
||||
pub(crate) paren_token: Option<syn::token::Paren>,
|
||||
pub(crate) body: T,
|
||||
}
|
||||
|
||||
crate::fold::impl_fold! {
|
||||
struct HdlAttr<T, KW,> {
|
||||
struct HdlAttr<T,> {
|
||||
pound_token: Pound,
|
||||
style: AttrStyle,
|
||||
bracket_token: syn::token::Bracket,
|
||||
kw: KW,
|
||||
hdl: kw::hdl,
|
||||
paren_token: Option<syn::token::Paren>,
|
||||
body: T,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
impl<T, KW> HdlAttr<T, KW> {
|
||||
pub(crate) fn split_body(self) -> (HdlAttr<(), KW>, T) {
|
||||
impl<T> HdlAttr<T> {
|
||||
pub(crate) fn split_body(self) -> (HdlAttr<()>, T) {
|
||||
let Self {
|
||||
pound_token,
|
||||
style,
|
||||
bracket_token,
|
||||
kw,
|
||||
hdl,
|
||||
paren_token,
|
||||
body,
|
||||
} = self;
|
||||
|
@ -123,19 +101,19 @@ impl<T, KW> HdlAttr<T, KW> {
|
|||
pound_token,
|
||||
style,
|
||||
bracket_token,
|
||||
kw,
|
||||
hdl,
|
||||
paren_token,
|
||||
body: (),
|
||||
},
|
||||
body,
|
||||
)
|
||||
}
|
||||
pub(crate) fn replace_body<T2>(self, body: T2) -> HdlAttr<T2, KW> {
|
||||
pub(crate) fn replace_body<T2>(self, body: T2) -> HdlAttr<T2> {
|
||||
let Self {
|
||||
pound_token,
|
||||
style,
|
||||
bracket_token,
|
||||
kw,
|
||||
hdl,
|
||||
paren_token,
|
||||
body: _,
|
||||
} = self;
|
||||
|
@ -143,20 +121,17 @@ impl<T, KW> HdlAttr<T, KW> {
|
|||
pound_token,
|
||||
style,
|
||||
bracket_token,
|
||||
kw,
|
||||
hdl,
|
||||
paren_token,
|
||||
body,
|
||||
}
|
||||
}
|
||||
pub(crate) fn as_ref(&self) -> HdlAttr<&T, KW>
|
||||
where
|
||||
KW: Clone,
|
||||
{
|
||||
pub(crate) fn as_ref(&self) -> HdlAttr<&T> {
|
||||
let Self {
|
||||
pound_token,
|
||||
style,
|
||||
bracket_token,
|
||||
ref kw,
|
||||
hdl,
|
||||
paren_token,
|
||||
ref body,
|
||||
} = *self;
|
||||
|
@ -164,20 +139,17 @@ impl<T, KW> HdlAttr<T, KW> {
|
|||
pound_token,
|
||||
style,
|
||||
bracket_token,
|
||||
kw: kw.clone(),
|
||||
hdl,
|
||||
paren_token,
|
||||
body,
|
||||
}
|
||||
}
|
||||
pub(crate) fn try_map<R, E, F: FnOnce(T) -> Result<R, E>>(
|
||||
self,
|
||||
f: F,
|
||||
) -> Result<HdlAttr<R, KW>, E> {
|
||||
pub(crate) fn try_map<R, E, F: FnOnce(T) -> Result<R, E>>(self, f: F) -> Result<HdlAttr<R>, E> {
|
||||
let Self {
|
||||
pound_token,
|
||||
style,
|
||||
bracket_token,
|
||||
kw,
|
||||
hdl,
|
||||
paren_token,
|
||||
body,
|
||||
} = self;
|
||||
|
@ -185,17 +157,17 @@ impl<T, KW> HdlAttr<T, KW> {
|
|||
pound_token,
|
||||
style,
|
||||
bracket_token,
|
||||
kw,
|
||||
hdl,
|
||||
paren_token,
|
||||
body: f(body)?,
|
||||
})
|
||||
}
|
||||
pub(crate) fn map<R, F: FnOnce(T) -> R>(self, f: F) -> HdlAttr<R, KW> {
|
||||
pub(crate) fn map<R, F: FnOnce(T) -> R>(self, f: F) -> HdlAttr<R> {
|
||||
let Self {
|
||||
pound_token,
|
||||
style,
|
||||
bracket_token,
|
||||
kw,
|
||||
hdl,
|
||||
paren_token,
|
||||
body,
|
||||
} = self;
|
||||
|
@ -203,7 +175,7 @@ impl<T, KW> HdlAttr<T, KW> {
|
|||
pound_token,
|
||||
style,
|
||||
bracket_token,
|
||||
kw,
|
||||
hdl,
|
||||
paren_token,
|
||||
body: f(body),
|
||||
}
|
||||
|
@ -211,32 +183,31 @@ impl<T, KW> HdlAttr<T, KW> {
|
|||
fn to_attr(&self) -> Attribute
|
||||
where
|
||||
T: ToTokens,
|
||||
KW: ToTokens,
|
||||
{
|
||||
parse_quote! { #self }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Default, KW: Default> Default for HdlAttr<T, KW> {
|
||||
impl<T: Default> Default for HdlAttr<T> {
|
||||
fn default() -> Self {
|
||||
T::default().into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T, KW: Default> From<T> for HdlAttr<T, KW> {
|
||||
impl<T> From<T> for HdlAttr<T> {
|
||||
fn from(body: T) -> Self {
|
||||
HdlAttr {
|
||||
pound_token: Default::default(),
|
||||
style: AttrStyle::Outer,
|
||||
bracket_token: Default::default(),
|
||||
kw: Default::default(),
|
||||
hdl: Default::default(),
|
||||
paren_token: Default::default(),
|
||||
body,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToTokens, KW: ToTokens + Spanned> ToTokens for HdlAttr<T, KW> {
|
||||
impl<T: ToTokens> ToTokens for HdlAttr<T> {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
self.pound_token.to_tokens(tokens);
|
||||
match self.style {
|
||||
|
@ -244,7 +215,7 @@ impl<T: ToTokens, KW: ToTokens + Spanned> ToTokens for HdlAttr<T, KW> {
|
|||
AttrStyle::Outer => {}
|
||||
};
|
||||
self.bracket_token.surround(tokens, |tokens| {
|
||||
self.kw.to_tokens(tokens);
|
||||
self.hdl.to_tokens(tokens);
|
||||
match self.paren_token {
|
||||
Some(paren_token) => {
|
||||
paren_token.surround(tokens, |tokens| self.body.to_tokens(tokens))
|
||||
|
@ -252,7 +223,7 @@ impl<T: ToTokens, KW: ToTokens + Spanned> ToTokens for HdlAttr<T, KW> {
|
|||
None => {
|
||||
let body = self.body.to_token_stream();
|
||||
if !body.is_empty() {
|
||||
syn::token::Paren(self.kw.span())
|
||||
syn::token::Paren(self.hdl.span)
|
||||
.surround(tokens, |tokens| tokens.extend([body]));
|
||||
}
|
||||
}
|
||||
|
@ -261,24 +232,18 @@ impl<T: ToTokens, KW: ToTokens + Spanned> ToTokens for HdlAttr<T, KW> {
|
|||
}
|
||||
}
|
||||
|
||||
fn is_hdl_attr<KW: CustomToken>(attr: &Attribute) -> bool {
|
||||
attr.path().is_ident(KW::IDENT_STR)
|
||||
fn is_hdl_attr(attr: &Attribute) -> bool {
|
||||
attr.path().is_ident("hdl")
|
||||
}
|
||||
|
||||
impl<T: Parse, KW: Parse> HdlAttr<T, KW> {
|
||||
fn parse_and_take_attr(attrs: &mut Vec<Attribute>) -> syn::Result<Option<Self>>
|
||||
where
|
||||
KW: ToTokens,
|
||||
{
|
||||
impl<T: Parse> HdlAttr<T> {
|
||||
fn parse_and_take_attr(attrs: &mut Vec<Attribute>) -> syn::Result<Option<Self>> {
|
||||
let mut retval = None;
|
||||
let mut errors = Errors::new();
|
||||
attrs.retain(|attr| {
|
||||
if let Ok(kw) = syn::parse2::<KW>(attr.path().to_token_stream()) {
|
||||
if is_hdl_attr(attr) {
|
||||
if retval.is_some() {
|
||||
errors.push(Error::new_spanned(
|
||||
attr,
|
||||
format_args!("more than one #[{}] attribute", kw.to_token_stream()),
|
||||
));
|
||||
errors.push(Error::new_spanned(attr, "more than one #[hdl] attribute"));
|
||||
}
|
||||
errors.unwrap_or_default(Self::parse_attr(attr).map(|v| retval = Some(v)));
|
||||
false
|
||||
|
@ -289,19 +254,13 @@ impl<T: Parse, KW: Parse> HdlAttr<T, KW> {
|
|||
errors.finish()?;
|
||||
Ok(retval)
|
||||
}
|
||||
fn parse_and_leave_attr(attrs: &[Attribute]) -> syn::Result<Option<Self>>
|
||||
where
|
||||
KW: ToTokens,
|
||||
{
|
||||
fn parse_and_leave_attr(attrs: &[Attribute]) -> syn::Result<Option<Self>> {
|
||||
let mut retval = None;
|
||||
let mut errors = Errors::new();
|
||||
for attr in attrs {
|
||||
if let Ok(kw) = syn::parse2::<KW>(attr.path().to_token_stream()) {
|
||||
if is_hdl_attr(attr) {
|
||||
if retval.is_some() {
|
||||
errors.push(Error::new_spanned(
|
||||
attr,
|
||||
format_args!("more than one #[{}] attribute", kw.to_token_stream()),
|
||||
));
|
||||
errors.push(Error::new_spanned(attr, "more than one #[hdl] attribute"));
|
||||
}
|
||||
errors.unwrap_or_default(Self::parse_attr(attr).map(|v| retval = Some(v)));
|
||||
}
|
||||
|
@ -322,7 +281,7 @@ impl<T: Parse, KW: Parse> HdlAttr<T, KW> {
|
|||
) -> syn::Result<Self> {
|
||||
let bracket_content;
|
||||
let bracket_token = bracketed!(bracket_content in input);
|
||||
let kw = bracket_content.parse()?;
|
||||
let hdl = bracket_content.parse()?;
|
||||
let paren_content;
|
||||
let body;
|
||||
let paren_token;
|
||||
|
@ -343,7 +302,7 @@ impl<T: Parse, KW: Parse> HdlAttr<T, KW> {
|
|||
pound_token,
|
||||
style,
|
||||
bracket_token,
|
||||
kw,
|
||||
hdl,
|
||||
paren_token,
|
||||
body,
|
||||
})
|
||||
|
@ -560,26 +519,6 @@ macro_rules! impl_extra_traits_for_options {
|
|||
) => {
|
||||
impl Copy for $option_enum_name {}
|
||||
|
||||
impl PartialEq for $option_enum_name {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.cmp(other).is_eq()
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for $option_enum_name {}
|
||||
|
||||
impl PartialOrd for $option_enum_name {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for $option_enum_name {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.variant().cmp(&other.variant())
|
||||
}
|
||||
}
|
||||
|
||||
impl quote::IdentFragment for $option_enum_name {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
let _ = f;
|
||||
|
@ -615,66 +554,6 @@ pub(crate) use impl_extra_traits_for_options;
|
|||
macro_rules! options {
|
||||
(
|
||||
#[options = $options_name:ident]
|
||||
$($tt:tt)*
|
||||
) => {
|
||||
crate::options! {
|
||||
#[options = $options_name, punct = syn::Token![,], allow_duplicates = false]
|
||||
$($tt)*
|
||||
}
|
||||
};
|
||||
(
|
||||
#[options = $options_name:ident, punct = $Punct:ty, allow_duplicates = true]
|
||||
$(#[$($enum_meta:tt)*])*
|
||||
$enum_vis:vis enum $option_enum_name:ident {
|
||||
$($Variant:ident($key:ident $(, $value:ty)?),)*
|
||||
}
|
||||
) => {
|
||||
crate::options! {
|
||||
#[options = $options_name, punct = $Punct, allow_duplicates = (true)]
|
||||
$(#[$($enum_meta)*])*
|
||||
$enum_vis enum $option_enum_name {
|
||||
$($Variant($key $(, $value)?),)*
|
||||
}
|
||||
}
|
||||
|
||||
impl Extend<$option_enum_name> for $options_name {
|
||||
fn extend<T: IntoIterator<Item = $option_enum_name>>(&mut self, iter: T) {
|
||||
iter.into_iter().for_each(|v| match v {
|
||||
$($option_enum_name::$Variant(v) => {
|
||||
self.$key = Some(v);
|
||||
})*
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<$option_enum_name> for $options_name {
|
||||
fn from_iter<T: IntoIterator<Item = $option_enum_name>>(iter: T) -> Self {
|
||||
let mut retval = Self::default();
|
||||
retval.extend(iter);
|
||||
retval
|
||||
}
|
||||
}
|
||||
|
||||
impl Extend<$options_name> for $options_name {
|
||||
fn extend<T: IntoIterator<Item = $options_name>>(&mut self, iter: T) {
|
||||
iter.into_iter().for_each(|v| {
|
||||
$(if let Some(v) = v.$key {
|
||||
self.$key = Some(v);
|
||||
})*
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl FromIterator<$options_name> for $options_name {
|
||||
fn from_iter<T: IntoIterator<Item = $options_name>>(iter: T) -> Self {
|
||||
let mut retval = Self::default();
|
||||
retval.extend(iter);
|
||||
retval
|
||||
}
|
||||
}
|
||||
};
|
||||
(
|
||||
#[options = $options_name:ident, punct = $Punct:ty, allow_duplicates = $allow_duplicates:expr]
|
||||
$(#[$($enum_meta:tt)*])*
|
||||
$enum_vis:vis enum $option_enum_name:ident {
|
||||
$($Variant:ident($key:ident $(, $value:ty)?),)*
|
||||
|
@ -688,11 +567,8 @@ macro_rules! options {
|
|||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
#[allow(non_snake_case)]
|
||||
$enum_vis struct $options_name {
|
||||
$(
|
||||
$enum_vis $key: Option<(crate::kw::$key, $(syn::token::Paren, $value)?)>,
|
||||
)*
|
||||
$($enum_vis $key: Option<(crate::kw::$key, $(syn::token::Paren, $value)?)>,)*
|
||||
}
|
||||
|
||||
crate::fold::impl_fold! {
|
||||
|
@ -701,43 +577,6 @@ macro_rules! options {
|
|||
}
|
||||
}
|
||||
|
||||
const _: () = {
|
||||
#[derive(Clone, Debug)]
|
||||
$enum_vis struct Iter($enum_vis $options_name);
|
||||
|
||||
impl IntoIterator for $options_name {
|
||||
type Item = $option_enum_name;
|
||||
type IntoIter = Iter;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
Iter(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for Iter {
|
||||
type Item = $option_enum_name;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
$(
|
||||
if let Some(value) = self.0.$key.take() {
|
||||
return Some($option_enum_name::$Variant(value));
|
||||
}
|
||||
)*
|
||||
None
|
||||
}
|
||||
|
||||
#[allow(unused_mut, unused_variables)]
|
||||
fn fold<B, F: FnMut(B, Self::Item) -> B>(mut self, mut init: B, mut f: F) -> B {
|
||||
$(
|
||||
if let Some(value) = self.0.$key.take() {
|
||||
init = f(init, $option_enum_name::$Variant(value));
|
||||
}
|
||||
)*
|
||||
init
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
impl syn::parse::Parse for $options_name {
|
||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||
#![allow(unused_mut, unused_variables, unreachable_code)]
|
||||
|
@ -746,7 +585,7 @@ macro_rules! options {
|
|||
let old_input = input.fork();
|
||||
match input.parse::<$option_enum_name>()? {
|
||||
$($option_enum_name::$Variant(v) => {
|
||||
if retval.$key.replace(v).is_some() && !$allow_duplicates {
|
||||
if retval.$key.replace(v).is_some() {
|
||||
return Err(old_input.error(concat!("duplicate ", stringify!($key), " option")));
|
||||
}
|
||||
})*
|
||||
|
@ -754,7 +593,7 @@ macro_rules! options {
|
|||
if input.is_empty() {
|
||||
break;
|
||||
}
|
||||
input.parse::<$Punct>()?;
|
||||
input.parse::<syn::Token![,]>()?;
|
||||
}
|
||||
Ok(retval)
|
||||
}
|
||||
|
@ -763,7 +602,7 @@ macro_rules! options {
|
|||
impl quote::ToTokens for $options_name {
|
||||
#[allow(unused_mut, unused_variables, unused_assignments)]
|
||||
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
|
||||
let mut separator: Option<$Punct> = None;
|
||||
let mut separator: Option<syn::Token![,]> = None;
|
||||
$(if let Some(v) = &self.$key {
|
||||
separator.to_tokens(tokens);
|
||||
separator = Some(Default::default());
|
||||
|
@ -834,24 +673,9 @@ macro_rules! options {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl $option_enum_name {
|
||||
#[allow(dead_code)]
|
||||
fn variant(&self) -> usize {
|
||||
#[repr(usize)]
|
||||
enum Variant {
|
||||
$($Variant,)*
|
||||
__Last, // so it doesn't complain about zero-variant enums
|
||||
}
|
||||
match *self {
|
||||
$(Self::$Variant(..) => Variant::$Variant as usize,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
use crate::hdl_type_alias::hdl_type_alias_impl;
|
||||
pub(crate) use options;
|
||||
|
||||
pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStream {
|
||||
|
@ -862,15 +686,6 @@ pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStr
|
|||
.suffix(".tmp.rs")
|
||||
.tempfile_in(out_dir)
|
||||
.unwrap();
|
||||
struct PrintOnPanic<'a>(&'a TokenStream);
|
||||
impl Drop for PrintOnPanic<'_> {
|
||||
fn drop(&mut self) {
|
||||
if std::thread::panicking() {
|
||||
println!("{}", self.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
let _print_on_panic = PrintOnPanic(&contents);
|
||||
let contents = prettyplease::unparse(&parse_quote! { #contents });
|
||||
let hash = <sha2::Sha256 as sha2::Digest>::digest(&contents);
|
||||
let hash = base16ct::HexDisplay(&hash[..5]);
|
||||
|
@ -891,33 +706,25 @@ pub(crate) fn outline_generated(contents: TokenStream, prefix: &str) -> TokenStr
|
|||
}
|
||||
}
|
||||
|
||||
fn hdl_module_impl(item: ItemFn) -> syn::Result<TokenStream> {
|
||||
let func = module::ModuleFn::parse_from_fn(item)?;
|
||||
let options = func.config_options();
|
||||
pub fn module(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
|
||||
let options = syn::parse2::<module::ConfigOptions>(attr)?;
|
||||
let options = HdlAttr::from(options);
|
||||
let func = syn::parse2::<module::ModuleFn>(quote! { #options #item })?;
|
||||
let mut contents = func.generate();
|
||||
if options.outline_generated.is_some() {
|
||||
if options.body.outline_generated.is_some() {
|
||||
contents = outline_generated(contents, "module-");
|
||||
}
|
||||
Ok(contents)
|
||||
}
|
||||
|
||||
pub fn hdl_module(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
|
||||
let kw = kw::hdl_module::default();
|
||||
hdl_module_impl(syn::parse2(quote! { #[#kw(#attr)] #item })?)
|
||||
}
|
||||
|
||||
pub fn hdl_attr(attr: TokenStream, item: TokenStream) -> syn::Result<TokenStream> {
|
||||
let kw = kw::hdl::default();
|
||||
let item = quote! { #[#kw(#attr)] #item };
|
||||
pub fn value_derive(item: TokenStream) -> syn::Result<TokenStream> {
|
||||
let item = syn::parse2::<Item>(item)?;
|
||||
match item {
|
||||
Item::Enum(item) => hdl_enum::hdl_enum(item),
|
||||
Item::Struct(item) => hdl_bundle::hdl_bundle(item),
|
||||
Item::Fn(item) => hdl_module_impl(item),
|
||||
Item::Type(item) => hdl_type_alias_impl(item),
|
||||
Item::Enum(item) => value_derive_enum::value_derive_enum(item),
|
||||
Item::Struct(item) => value_derive_struct::value_derive_struct(item),
|
||||
_ => Err(syn::Error::new(
|
||||
Span::call_site(),
|
||||
"top-level #[hdl] can only be used on structs, enums, type aliases, or functions",
|
||||
"derive(Value) can only be used on structs or enums",
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
hdl_type_common::{ParsedGenerics, SplitForImpl},
|
||||
kw,
|
||||
is_hdl_attr,
|
||||
module::transform_body::{HdlLet, HdlLetKindIO},
|
||||
options, Errors, HdlAttr, PairsIterExt,
|
||||
};
|
||||
|
@ -10,6 +9,7 @@ use proc_macro2::TokenStream;
|
|||
use quote::{format_ident, quote, quote_spanned, ToTokens};
|
||||
use std::collections::HashSet;
|
||||
use syn::{
|
||||
parse::{Parse, ParseStream},
|
||||
parse_quote,
|
||||
visit::{visit_pat, Visit},
|
||||
Attribute, Block, ConstParam, Error, FnArg, GenericParam, Generics, Ident, ItemFn, ItemStruct,
|
||||
|
@ -57,39 +57,26 @@ impl Visit<'_> for CheckNameConflictsWithModuleBuilderVisitor<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
fn retain_struct_attrs<F: FnMut(&Attribute) -> bool>(item: &mut ItemStruct, mut f: F) {
|
||||
item.attrs.retain(&mut f);
|
||||
for field in item.fields.iter_mut() {
|
||||
field.attrs.retain(&mut f);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) type ModuleIO = HdlLet<HdlLetKindIO>;
|
||||
|
||||
struct ModuleFnModule {
|
||||
pub(crate) struct ModuleFn {
|
||||
attrs: Vec<Attribute>,
|
||||
config_options: HdlAttr<ConfigOptions, kw::hdl_module>,
|
||||
config_options: HdlAttr<ConfigOptions>,
|
||||
module_kind: ModuleKind,
|
||||
vis: Visibility,
|
||||
sig: Signature,
|
||||
block: Box<Block>,
|
||||
struct_generics: ParsedGenerics,
|
||||
the_struct: TokenStream,
|
||||
io: Vec<ModuleIO>,
|
||||
struct_generics: Generics,
|
||||
}
|
||||
|
||||
enum ModuleFnImpl {
|
||||
Module(ModuleFnModule),
|
||||
Fn {
|
||||
attrs: Vec<Attribute>,
|
||||
config_options: HdlAttr<ConfigOptions, kw::hdl>,
|
||||
vis: Visibility,
|
||||
sig: Signature,
|
||||
block: Box<Block>,
|
||||
},
|
||||
}
|
||||
|
||||
options! {
|
||||
pub(crate) enum HdlOrHdlModule {
|
||||
Hdl(hdl),
|
||||
HdlModule(hdl_module),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ModuleFn(ModuleFnImpl);
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
|
||||
pub(crate) enum ModuleKind {
|
||||
Extern,
|
||||
|
@ -109,25 +96,14 @@ impl Visit<'_> for ContainsSkippedIdent<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
impl ModuleFn {
|
||||
pub(crate) fn config_options(&self) -> ConfigOptions {
|
||||
let (ModuleFnImpl::Module(ModuleFnModule {
|
||||
config_options: HdlAttr { body, .. },
|
||||
..
|
||||
})
|
||||
| ModuleFnImpl::Fn {
|
||||
config_options: HdlAttr { body, .. },
|
||||
..
|
||||
}) = &self.0;
|
||||
body.clone()
|
||||
}
|
||||
pub(crate) fn parse_from_fn(item: ItemFn) -> syn::Result<Self> {
|
||||
impl Parse for ModuleFn {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let ItemFn {
|
||||
mut attrs,
|
||||
vis,
|
||||
mut sig,
|
||||
block,
|
||||
} = item;
|
||||
} = input.parse()?;
|
||||
let Signature {
|
||||
ref constness,
|
||||
ref asyncness,
|
||||
|
@ -142,60 +118,43 @@ impl ModuleFn {
|
|||
ref output,
|
||||
} = sig;
|
||||
let mut errors = Errors::new();
|
||||
let Some(mut config_options) =
|
||||
errors.unwrap_or_default(
|
||||
HdlAttr::<ConfigOptions, HdlOrHdlModule>::parse_and_take_attr(&mut attrs),
|
||||
)
|
||||
else {
|
||||
errors.error(sig.ident, "missing #[hdl] or #[hdl_module] attribute");
|
||||
errors.finish()?;
|
||||
unreachable!();
|
||||
};
|
||||
let config_options = errors
|
||||
.unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs))
|
||||
.unwrap_or_default();
|
||||
let ConfigOptions {
|
||||
outline_generated: _,
|
||||
extern_,
|
||||
} = config_options.body;
|
||||
let module_kind = match (config_options.kw, extern_) {
|
||||
(HdlOrHdlModule::Hdl(_), None) => None,
|
||||
(HdlOrHdlModule::Hdl(_), Some(extern2)) => {
|
||||
config_options.body.extern_ = None;
|
||||
errors.error(
|
||||
extern2.0,
|
||||
"extern can only be used as #[hdl_module(extern)]",
|
||||
);
|
||||
None
|
||||
}
|
||||
(HdlOrHdlModule::HdlModule(_), None) => Some(ModuleKind::Normal),
|
||||
(HdlOrHdlModule::HdlModule(_), Some(_)) => Some(ModuleKind::Extern),
|
||||
let module_kind = match extern_ {
|
||||
Some(_) => ModuleKind::Extern,
|
||||
None => ModuleKind::Normal,
|
||||
};
|
||||
if let HdlOrHdlModule::HdlModule(_) = config_options.kw {
|
||||
for fn_arg in inputs {
|
||||
match fn_arg {
|
||||
FnArg::Receiver(_) => {
|
||||
errors.push(syn::Error::new_spanned(fn_arg, "self not allowed here"));
|
||||
}
|
||||
FnArg::Typed(fn_arg) => {
|
||||
visit_pat(
|
||||
&mut CheckNameConflictsWithModuleBuilderVisitor {
|
||||
errors: &mut errors,
|
||||
},
|
||||
&fn_arg.pat,
|
||||
);
|
||||
}
|
||||
for fn_arg in inputs {
|
||||
match fn_arg {
|
||||
FnArg::Receiver(_) => {
|
||||
errors.push(syn::Error::new_spanned(fn_arg, "self not allowed here"));
|
||||
}
|
||||
FnArg::Typed(fn_arg) => {
|
||||
visit_pat(
|
||||
&mut CheckNameConflictsWithModuleBuilderVisitor {
|
||||
errors: &mut errors,
|
||||
},
|
||||
&fn_arg.pat,
|
||||
);
|
||||
}
|
||||
}
|
||||
if let Some(constness) = constness {
|
||||
errors.push(syn::Error::new_spanned(constness, "const not allowed here"));
|
||||
}
|
||||
if let Some(asyncness) = asyncness {
|
||||
errors.push(syn::Error::new_spanned(asyncness, "async not allowed here"));
|
||||
}
|
||||
if let Some(unsafety) = unsafety {
|
||||
errors.push(syn::Error::new_spanned(unsafety, "unsafe not allowed here"));
|
||||
}
|
||||
if let Some(abi) = abi {
|
||||
errors.push(syn::Error::new_spanned(abi, "extern not allowed here"));
|
||||
}
|
||||
}
|
||||
if let Some(constness) = constness {
|
||||
errors.push(syn::Error::new_spanned(constness, "const not allowed here"));
|
||||
}
|
||||
if let Some(asyncness) = asyncness {
|
||||
errors.push(syn::Error::new_spanned(asyncness, "async not allowed here"));
|
||||
}
|
||||
if let Some(unsafety) = unsafety {
|
||||
errors.push(syn::Error::new_spanned(unsafety, "unsafe not allowed here"));
|
||||
}
|
||||
if let Some(abi) = abi {
|
||||
errors.push(syn::Error::new_spanned(abi, "extern not allowed here"));
|
||||
}
|
||||
let mut skipped_idents = HashSet::new();
|
||||
let struct_generic_params = generics
|
||||
|
@ -203,17 +162,14 @@ impl ModuleFn {
|
|||
.pairs_mut()
|
||||
.filter_map_pair_value_mut(|v| match v {
|
||||
GenericParam::Lifetime(LifetimeParam { attrs, .. }) => {
|
||||
errors.unwrap_or_default(
|
||||
HdlAttr::<crate::kw::skip, kw::hdl>::parse_and_take_attr(attrs),
|
||||
);
|
||||
errors
|
||||
.unwrap_or_default(HdlAttr::<crate::kw::skip>::parse_and_take_attr(attrs));
|
||||
None
|
||||
}
|
||||
GenericParam::Type(TypeParam { attrs, ident, .. })
|
||||
| GenericParam::Const(ConstParam { attrs, ident, .. }) => {
|
||||
if errors
|
||||
.unwrap_or_default(
|
||||
HdlAttr::<crate::kw::skip, kw::hdl>::parse_and_take_attr(attrs),
|
||||
)
|
||||
.unwrap_or_default(HdlAttr::<crate::kw::skip>::parse_and_take_attr(attrs))
|
||||
.is_some()
|
||||
{
|
||||
skipped_idents.insert(ident.clone());
|
||||
|
@ -227,7 +183,6 @@ impl ModuleFn {
|
|||
let struct_where_clause = generics
|
||||
.where_clause
|
||||
.as_mut()
|
||||
.filter(|_| matches!(config_options.kw, HdlOrHdlModule::HdlModule(_)))
|
||||
.map(|where_clause| WhereClause {
|
||||
where_token: where_clause.where_token,
|
||||
predicates: where_clause
|
||||
|
@ -250,158 +205,49 @@ impl ModuleFn {
|
|||
})
|
||||
.collect(),
|
||||
});
|
||||
let struct_generics = if let HdlOrHdlModule::HdlModule(_) = config_options.kw {
|
||||
let mut struct_generics = Generics {
|
||||
lt_token: generics.lt_token,
|
||||
params: struct_generic_params,
|
||||
gt_token: generics.gt_token,
|
||||
where_clause: struct_where_clause,
|
||||
};
|
||||
if let Some(variadic) = variadic {
|
||||
errors.push(syn::Error::new_spanned(variadic, "... not allowed here"));
|
||||
}
|
||||
if !matches!(output, ReturnType::Default) {
|
||||
errors.push(syn::Error::new_spanned(
|
||||
output,
|
||||
"return type not allowed here",
|
||||
));
|
||||
}
|
||||
errors.ok(ParsedGenerics::parse(&mut struct_generics))
|
||||
} else {
|
||||
Some(ParsedGenerics::default())
|
||||
let struct_generics = Generics {
|
||||
lt_token: generics.lt_token,
|
||||
params: struct_generic_params,
|
||||
gt_token: generics.gt_token,
|
||||
where_clause: struct_where_clause,
|
||||
};
|
||||
let body_results = struct_generics.as_ref().and_then(|struct_generics| {
|
||||
errors.ok(transform_body::transform_body(
|
||||
module_kind,
|
||||
block,
|
||||
struct_generics,
|
||||
))
|
||||
});
|
||||
errors.finish()?;
|
||||
let struct_generics = struct_generics.unwrap();
|
||||
let (block, io) = body_results.unwrap();
|
||||
let config_options = match config_options {
|
||||
HdlAttr {
|
||||
pound_token,
|
||||
style,
|
||||
bracket_token,
|
||||
kw: HdlOrHdlModule::Hdl((kw,)),
|
||||
paren_token,
|
||||
body,
|
||||
} => {
|
||||
debug_assert!(io.is_empty());
|
||||
return Ok(Self(ModuleFnImpl::Fn {
|
||||
attrs,
|
||||
config_options: HdlAttr {
|
||||
pound_token,
|
||||
style,
|
||||
bracket_token,
|
||||
kw,
|
||||
paren_token,
|
||||
body,
|
||||
},
|
||||
vis,
|
||||
sig,
|
||||
block,
|
||||
}));
|
||||
}
|
||||
HdlAttr {
|
||||
pound_token,
|
||||
style,
|
||||
bracket_token,
|
||||
kw: HdlOrHdlModule::HdlModule((kw,)),
|
||||
paren_token,
|
||||
body,
|
||||
} => HdlAttr {
|
||||
pound_token,
|
||||
style,
|
||||
bracket_token,
|
||||
kw,
|
||||
paren_token,
|
||||
body,
|
||||
},
|
||||
};
|
||||
let (_struct_impl_generics, _struct_type_generics, struct_where_clause) =
|
||||
struct_generics.split_for_impl();
|
||||
let struct_where_clause: Option<WhereClause> = parse_quote! { #struct_where_clause };
|
||||
if let Some(struct_where_clause) = &struct_where_clause {
|
||||
sig.generics
|
||||
.where_clause
|
||||
.get_or_insert_with(|| WhereClause {
|
||||
where_token: struct_where_clause.where_token,
|
||||
predicates: Default::default(),
|
||||
})
|
||||
.predicates
|
||||
.extend(struct_where_clause.predicates.clone());
|
||||
if let Some(variadic) = variadic {
|
||||
errors.push(syn::Error::new_spanned(variadic, "... not allowed here"));
|
||||
}
|
||||
let fn_name = &sig.ident;
|
||||
let io_flips = io
|
||||
.iter()
|
||||
.map(|io| match io.kind.kind {
|
||||
ModuleIOKind::Input((input,)) => quote_spanned! {input.span=>
|
||||
#[hdl(flip)]
|
||||
},
|
||||
ModuleIOKind::Output(_) => quote! {},
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let io_types = io.iter().map(|io| &io.kind.ty).collect::<Vec<_>>();
|
||||
let io_names = io.iter().map(|io| &io.name).collect::<Vec<_>>();
|
||||
let the_struct: ItemStruct = parse_quote! {
|
||||
#[allow(non_camel_case_types)]
|
||||
#[hdl(no_runtime_generics, no_static)]
|
||||
#vis struct #fn_name #struct_generics #struct_where_clause {
|
||||
#(
|
||||
#io_flips
|
||||
#vis #io_names: #io_types,)*
|
||||
}
|
||||
};
|
||||
let the_struct = crate::hdl_bundle::hdl_bundle(the_struct)?;
|
||||
Ok(Self(ModuleFnImpl::Module(ModuleFnModule {
|
||||
attrs,
|
||||
config_options,
|
||||
module_kind: module_kind.unwrap(),
|
||||
vis,
|
||||
sig,
|
||||
block,
|
||||
struct_generics,
|
||||
the_struct,
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleFn {
|
||||
pub(crate) fn generate(self) -> TokenStream {
|
||||
let ModuleFnModule {
|
||||
if !matches!(output, ReturnType::Default) {
|
||||
errors.push(syn::Error::new_spanned(
|
||||
output,
|
||||
"return type not allowed here",
|
||||
));
|
||||
}
|
||||
let body_results = errors.ok(transform_body::transform_body(module_kind, block));
|
||||
errors.finish()?;
|
||||
let (block, io) = body_results.unwrap();
|
||||
Ok(Self {
|
||||
attrs,
|
||||
config_options,
|
||||
module_kind,
|
||||
vis,
|
||||
sig,
|
||||
block,
|
||||
io,
|
||||
struct_generics,
|
||||
the_struct,
|
||||
} = match self.0 {
|
||||
ModuleFnImpl::Module(v) => v,
|
||||
ModuleFnImpl::Fn {
|
||||
attrs,
|
||||
config_options,
|
||||
vis,
|
||||
sig,
|
||||
block,
|
||||
} => {
|
||||
let ConfigOptions {
|
||||
outline_generated: _,
|
||||
extern_: _,
|
||||
} = config_options.body;
|
||||
return ItemFn {
|
||||
attrs,
|
||||
vis,
|
||||
sig,
|
||||
block,
|
||||
}
|
||||
.into_token_stream();
|
||||
}
|
||||
};
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ModuleFn {
|
||||
pub(crate) fn generate(self) -> TokenStream {
|
||||
let Self {
|
||||
attrs,
|
||||
config_options,
|
||||
module_kind,
|
||||
vis,
|
||||
sig,
|
||||
block,
|
||||
io,
|
||||
struct_generics,
|
||||
} = self;
|
||||
let ConfigOptions {
|
||||
outline_generated: _,
|
||||
extern_: _,
|
||||
|
@ -427,18 +273,19 @@ impl ModuleFn {
|
|||
});
|
||||
name
|
||||
}));
|
||||
let module_kind_value = match module_kind {
|
||||
ModuleKind::Extern => quote! { ::fayalite::module::ModuleKind::Extern },
|
||||
ModuleKind::Normal => quote! { ::fayalite::module::ModuleKind::Normal },
|
||||
let module_kind_ty = match module_kind {
|
||||
ModuleKind::Extern => quote! { ::fayalite::module::ExternModule },
|
||||
ModuleKind::Normal => quote! { ::fayalite::module::NormalModule },
|
||||
};
|
||||
let fn_name = &outer_sig.ident;
|
||||
let (_struct_impl_generics, struct_type_generics, _struct_where_clause) =
|
||||
let (_struct_impl_generics, struct_type_generics, struct_where_clause) =
|
||||
struct_generics.split_for_impl();
|
||||
let struct_ty = quote! {#fn_name #struct_type_generics};
|
||||
body_sig.ident = parse_quote! {__body};
|
||||
body_sig
|
||||
.inputs
|
||||
.insert(0, parse_quote! { m: &::fayalite::module::ModuleBuilder });
|
||||
body_sig.inputs.insert(
|
||||
0,
|
||||
parse_quote! {m: &mut ::fayalite::module::ModuleBuilder<#struct_ty, #module_kind_ty>},
|
||||
);
|
||||
let body_fn = ItemFn {
|
||||
attrs: vec![],
|
||||
vis: Visibility::Inherited,
|
||||
|
@ -447,26 +294,50 @@ impl ModuleFn {
|
|||
};
|
||||
outer_sig.output =
|
||||
parse_quote! {-> ::fayalite::intern::Interned<::fayalite::module::Module<#struct_ty>>};
|
||||
let io_flips = io
|
||||
.iter()
|
||||
.map(|io| match io.kind.kind {
|
||||
ModuleIOKind::Input((input,)) => quote_spanned! {input.span=>
|
||||
#[hdl(flip)]
|
||||
},
|
||||
ModuleIOKind::Output(_) => quote! {},
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let io_types = io.iter().map(|io| &io.kind.ty).collect::<Vec<_>>();
|
||||
let io_names = io.iter().map(|io| &io.name).collect::<Vec<_>>();
|
||||
let fn_name_str = fn_name.to_string();
|
||||
let (_, body_type_generics, _) = body_fn.sig.generics.split_for_impl();
|
||||
let body_turbofish_type_generics = body_type_generics.as_turbofish();
|
||||
let body_lambda = if param_names.is_empty() {
|
||||
quote! {
|
||||
__body #body_turbofish_type_generics
|
||||
}
|
||||
} else {
|
||||
quote! {
|
||||
|m| __body #body_turbofish_type_generics(m, #(#param_names,)*)
|
||||
}
|
||||
};
|
||||
let block = parse_quote! {{
|
||||
#body_fn
|
||||
::fayalite::module::ModuleBuilder::run(
|
||||
#fn_name_str,
|
||||
#module_kind_value,
|
||||
#body_lambda,
|
||||
)
|
||||
::fayalite::module::ModuleBuilder::run(#fn_name_str, |m| __body #body_turbofish_type_generics(m, #(#param_names,)*))
|
||||
}};
|
||||
let static_type = io.iter().all(|io| io.kind.ty_expr.is_none());
|
||||
let struct_options = if static_type {
|
||||
quote! { #[hdl(static)] }
|
||||
} else {
|
||||
quote! {}
|
||||
};
|
||||
let the_struct: ItemStruct = parse_quote! {
|
||||
#[derive(::fayalite::__std::clone::Clone,
|
||||
::fayalite::__std::hash::Hash,
|
||||
::fayalite::__std::cmp::PartialEq,
|
||||
::fayalite::__std::cmp::Eq,
|
||||
::fayalite::__std::fmt::Debug)]
|
||||
#[allow(non_camel_case_types)]
|
||||
#struct_options
|
||||
#vis struct #fn_name #struct_generics #struct_where_clause {
|
||||
#(
|
||||
#io_flips
|
||||
#vis #io_names: #io_types,)*
|
||||
}
|
||||
};
|
||||
let mut struct_without_hdl_attrs = the_struct.clone();
|
||||
let mut struct_without_derives = the_struct;
|
||||
retain_struct_attrs(&mut struct_without_hdl_attrs, |attr| !is_hdl_attr(attr));
|
||||
retain_struct_attrs(&mut struct_without_derives, |attr| {
|
||||
!attr.path().is_ident("derive")
|
||||
});
|
||||
let outer_fn = ItemFn {
|
||||
attrs,
|
||||
vis,
|
||||
|
@ -474,7 +345,10 @@ impl ModuleFn {
|
|||
block,
|
||||
};
|
||||
let mut retval = outer_fn.into_token_stream();
|
||||
retval.extend(the_struct);
|
||||
struct_without_hdl_attrs.to_tokens(&mut retval);
|
||||
retval.extend(
|
||||
crate::value_derive_struct::value_derive_struct(struct_without_derives).unwrap(),
|
||||
);
|
||||
retval
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,14 +2,11 @@
|
|||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
fold::{impl_fold, DoFold},
|
||||
hdl_type_common::{
|
||||
known_items, ParseFailed, ParseTypes, ParsedGenerics, ParsedType, TypesParser,
|
||||
},
|
||||
is_hdl_attr, kw,
|
||||
module::{check_name_conflicts_with_module_builder, ModuleIO, ModuleIOKind, ModuleKind},
|
||||
options, Errors, HdlAttr,
|
||||
};
|
||||
use num_bigint::BigInt;
|
||||
use num_bigint::{BigInt, Sign};
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::{quote, quote_spanned, ToTokens};
|
||||
use std::{borrow::Borrow, convert::Infallible};
|
||||
|
@ -34,7 +31,6 @@ options! {
|
|||
Instance(instance),
|
||||
RegBuilder(reg_builder),
|
||||
Wire(wire),
|
||||
IncompleteWire(incomplete_wire),
|
||||
Memory(memory),
|
||||
MemoryArray(memory_array),
|
||||
MemoryWithInit(memory_with_init),
|
||||
|
@ -91,9 +87,9 @@ macro_rules! with_debug_clone_and_fold {
|
|||
pub(crate) use with_debug_clone_and_fold;
|
||||
|
||||
with_debug_clone_and_fold! {
|
||||
pub(crate) struct HdlLetKindIO<Kind = ModuleIOKind, Ty = ParsedType> {
|
||||
pub(crate) struct HdlLetKindIO<Kind = ModuleIOKind> {
|
||||
pub(crate) colon_token: Token![:],
|
||||
pub(crate) ty: Box<Ty>,
|
||||
pub(crate) ty: Box<Type>,
|
||||
pub(crate) m: kw::m,
|
||||
pub(crate) dot_token: Token![.],
|
||||
pub(crate) kind: Kind,
|
||||
|
@ -102,32 +98,6 @@ with_debug_clone_and_fold! {
|
|||
}
|
||||
}
|
||||
|
||||
impl<Kind: Clone, T: ParseTypes<I>, I> ParseTypes<HdlLetKindIO<Kind, I>> for HdlLetKindIO<Kind, T> {
|
||||
fn parse_types(
|
||||
input: &mut HdlLetKindIO<Kind, I>,
|
||||
parser: &mut TypesParser<'_>,
|
||||
) -> Result<Self, ParseFailed> {
|
||||
let HdlLetKindIO {
|
||||
colon_token,
|
||||
ty,
|
||||
m,
|
||||
dot_token,
|
||||
kind,
|
||||
paren,
|
||||
ty_expr,
|
||||
} = input;
|
||||
Ok(Self {
|
||||
colon_token: *colon_token,
|
||||
ty: ParseTypes::parse_types(ty, parser)?,
|
||||
m: *m,
|
||||
dot_token: *dot_token,
|
||||
kind: kind.clone(),
|
||||
paren: *paren,
|
||||
ty_expr: ty_expr.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn parse_single_fn_arg(input: ParseStream) -> syn::Result<Box<Expr>> {
|
||||
let retval = input.parse()?;
|
||||
let _: Option<Token![,]> = input.parse()?;
|
||||
|
@ -175,19 +145,17 @@ impl<Kind: ToTokens> HdlLetKindToTokens for HdlLetKindIO<Kind> {
|
|||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct HdlLetKindInstance {
|
||||
pub(crate) m: kw::m,
|
||||
pub(crate) dot_token: Token![.],
|
||||
pub(crate) instance: kw::instance,
|
||||
pub(crate) paren: Paren,
|
||||
pub(crate) module: Box<Expr>,
|
||||
}
|
||||
|
||||
impl ParseTypes<Self> for HdlLetKindInstance {
|
||||
fn parse_types(input: &mut Self, _parser: &mut TypesParser<'_>) -> Result<Self, ParseFailed> {
|
||||
Ok(input.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl_fold! {
|
||||
struct HdlLetKindInstance<> {
|
||||
m: kw::m,
|
||||
dot_token: Token![.],
|
||||
instance: kw::instance,
|
||||
paren: Paren,
|
||||
module: Box<Expr>,
|
||||
|
@ -199,10 +167,14 @@ impl HdlLetKindToTokens for HdlLetKindInstance {
|
|||
|
||||
fn expr_to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self {
|
||||
m,
|
||||
dot_token,
|
||||
instance,
|
||||
paren,
|
||||
module,
|
||||
} = self;
|
||||
m.to_tokens(tokens);
|
||||
dot_token.to_tokens(tokens);
|
||||
instance.to_tokens(tokens);
|
||||
paren.surround(tokens, |tokens| module.to_tokens(tokens));
|
||||
}
|
||||
|
@ -265,6 +237,11 @@ pub(crate) enum RegBuilderReset {
|
|||
paren: Paren,
|
||||
init_expr: Box<Expr>,
|
||||
},
|
||||
ResetDefault {
|
||||
dot_token: Token![.],
|
||||
reset_default: kw::reset_default,
|
||||
paren: Paren,
|
||||
},
|
||||
}
|
||||
|
||||
impl_fold! {
|
||||
|
@ -281,6 +258,11 @@ impl_fold! {
|
|||
paren: Paren,
|
||||
init_expr: Box<Expr>,
|
||||
},
|
||||
ResetDefault {
|
||||
dot_token: Token![.],
|
||||
reset_default: kw::reset_default,
|
||||
paren: Paren,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -302,6 +284,11 @@ impl Parse for RegBuilderReset {
|
|||
paren: parenthesized!(paren_contents in input),
|
||||
init_expr: paren_contents.call(parse_single_fn_arg)?,
|
||||
}),
|
||||
RegBuilderMethod::ResetDefault(reset_default) => Ok(Self::ResetDefault {
|
||||
dot_token,
|
||||
reset_default,
|
||||
paren: parenthesized!(paren_contents in input),
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -329,6 +316,15 @@ impl ToTokens for RegBuilderReset {
|
|||
reset.to_tokens(tokens);
|
||||
paren.surround(tokens, |tokens| init_expr.to_tokens(tokens));
|
||||
}
|
||||
RegBuilderReset::ResetDefault {
|
||||
dot_token,
|
||||
reset_default,
|
||||
paren,
|
||||
} => {
|
||||
dot_token.to_tokens(tokens);
|
||||
reset_default.to_tokens(tokens);
|
||||
paren.surround(tokens, |_| {});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -377,27 +373,27 @@ make_builder_method_enum! {
|
|||
NoReset(no_reset),
|
||||
#[cond = need_reset]
|
||||
Reset(reset),
|
||||
#[cond = need_reset]
|
||||
ResetDefault(reset_default),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct HdlLetKindRegBuilder {
|
||||
pub(crate) ty: Option<(Token![:], Box<Type>)>,
|
||||
pub(crate) m: kw::m,
|
||||
pub(crate) dot_token: Token![.],
|
||||
pub(crate) reg_builder: kw::reg_builder,
|
||||
pub(crate) reg_builder_paren: Paren,
|
||||
pub(crate) clock_domain: Option<RegBuilderClockDomain>,
|
||||
pub(crate) reset: RegBuilderReset,
|
||||
}
|
||||
|
||||
impl ParseTypes<Self> for HdlLetKindRegBuilder {
|
||||
fn parse_types(input: &mut Self, _parser: &mut TypesParser<'_>) -> Result<Self, ParseFailed> {
|
||||
Ok(input.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl_fold! {
|
||||
struct HdlLetKindRegBuilder<> {
|
||||
ty: Option<(Token![:], Box<Type>)>,
|
||||
m: kw::m,
|
||||
dot_token: Token![.],
|
||||
reg_builder: kw::reg_builder,
|
||||
reg_builder_paren: Paren,
|
||||
clock_domain: Option<RegBuilderClockDomain>,
|
||||
|
@ -410,26 +406,32 @@ impl HdlLetKindRegBuilder {
|
|||
input: ParseStream,
|
||||
parsed_ty: Option<(Token![:], Box<Type>)>,
|
||||
_after_ty: Token![=],
|
||||
m_dot: Option<(kw::m, Token![.])>,
|
||||
m: kw::m,
|
||||
dot_token: Token![.],
|
||||
reg_builder: kw::reg_builder,
|
||||
) -> syn::Result<Self> {
|
||||
check_empty_m_dot(m_dot, reg_builder)?;
|
||||
let _reg_builder_paren_inner;
|
||||
let reg_builder_paren = parenthesized!(_reg_builder_paren_inner in input);
|
||||
let mut clock_domain = None;
|
||||
match RegBuilderMethod::parse_dot_prefixed(&input.fork(), true, true)?.1 {
|
||||
RegBuilderMethod::ClockDomain(_) => clock_domain = Some(input.parse()?),
|
||||
RegBuilderMethod::NoReset(_) | RegBuilderMethod::Reset(_) => {}
|
||||
RegBuilderMethod::NoReset(_)
|
||||
| RegBuilderMethod::Reset(_)
|
||||
| RegBuilderMethod::ResetDefault(_) => {}
|
||||
}
|
||||
let reset = input.parse()?;
|
||||
if clock_domain.is_none() {
|
||||
match RegBuilderMethod::parse_dot_prefixed(&input.fork(), true, false)?.1 {
|
||||
RegBuilderMethod::ClockDomain(_) => clock_domain = Some(input.parse()?),
|
||||
RegBuilderMethod::NoReset(_) | RegBuilderMethod::Reset(_) => unreachable!(),
|
||||
RegBuilderMethod::NoReset(_)
|
||||
| RegBuilderMethod::Reset(_)
|
||||
| RegBuilderMethod::ResetDefault(_) => unreachable!(),
|
||||
}
|
||||
}
|
||||
Ok(Self {
|
||||
ty: parsed_ty,
|
||||
m,
|
||||
dot_token,
|
||||
reg_builder,
|
||||
reg_builder_paren,
|
||||
clock_domain,
|
||||
|
@ -449,11 +451,15 @@ impl HdlLetKindToTokens for HdlLetKindRegBuilder {
|
|||
fn expr_to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self {
|
||||
ty: _,
|
||||
m,
|
||||
dot_token,
|
||||
reg_builder,
|
||||
reg_builder_paren,
|
||||
clock_domain,
|
||||
reset,
|
||||
} = self;
|
||||
m.to_tokens(tokens);
|
||||
dot_token.to_tokens(tokens);
|
||||
reg_builder.to_tokens(tokens);
|
||||
reg_builder_paren.surround(tokens, |_tokens| {});
|
||||
clock_domain.to_tokens(tokens);
|
||||
|
@ -464,20 +470,18 @@ impl HdlLetKindToTokens for HdlLetKindRegBuilder {
|
|||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct HdlLetKindWire {
|
||||
pub(crate) ty: Option<(Token![:], Box<Type>)>,
|
||||
pub(crate) m: kw::m,
|
||||
pub(crate) dot_token: Token![.],
|
||||
pub(crate) wire: kw::wire,
|
||||
pub(crate) paren: Paren,
|
||||
pub(crate) ty_expr: Option<Box<Expr>>,
|
||||
}
|
||||
|
||||
impl ParseTypes<Self> for HdlLetKindWire {
|
||||
fn parse_types(input: &mut Self, _parser: &mut TypesParser<'_>) -> Result<Self, ParseFailed> {
|
||||
Ok(input.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl_fold! {
|
||||
struct HdlLetKindWire<> {
|
||||
ty: Option<(Token![:], Box<Type>)>,
|
||||
m: kw::m,
|
||||
dot_token: Token![.],
|
||||
wire: kw::wire,
|
||||
paren: Paren,
|
||||
ty_expr: Option<Box<Expr>>,
|
||||
|
@ -495,50 +499,19 @@ impl HdlLetKindToTokens for HdlLetKindWire {
|
|||
fn expr_to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self {
|
||||
ty: _,
|
||||
m,
|
||||
dot_token,
|
||||
wire,
|
||||
paren,
|
||||
ty_expr,
|
||||
} = self;
|
||||
m.to_tokens(tokens);
|
||||
dot_token.to_tokens(tokens);
|
||||
wire.to_tokens(tokens);
|
||||
paren.surround(tokens, |tokens| ty_expr.to_tokens(tokens));
|
||||
}
|
||||
}
|
||||
|
||||
options! {
|
||||
pub(crate) enum LetFnKindIncomplete {
|
||||
IncompleteWire(incomplete_wire),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct HdlLetKindIncomplete {
|
||||
pub(crate) kind: LetFnKindIncomplete,
|
||||
pub(crate) paren: Paren,
|
||||
}
|
||||
|
||||
impl ParseTypes<Self> for HdlLetKindIncomplete {
|
||||
fn parse_types(input: &mut Self, _parser: &mut TypesParser<'_>) -> Result<Self, ParseFailed> {
|
||||
Ok(input.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl_fold! {
|
||||
struct HdlLetKindIncomplete<> {
|
||||
kind: LetFnKindIncomplete,
|
||||
paren: Paren,
|
||||
}
|
||||
}
|
||||
|
||||
impl HdlLetKindToTokens for HdlLetKindIncomplete {
|
||||
fn ty_to_tokens(&self, _tokens: &mut TokenStream) {}
|
||||
|
||||
fn expr_to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self { kind, paren } = self;
|
||||
kind.to_tokens(tokens);
|
||||
paren.surround(tokens, |_| {});
|
||||
}
|
||||
}
|
||||
|
||||
options! {
|
||||
pub(crate) enum MemoryFnName {
|
||||
Memory(memory),
|
||||
|
@ -654,18 +627,16 @@ impl ToTokens for MemoryFn {
|
|||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct HdlLetKindMemory {
|
||||
pub(crate) ty: Option<(Token![:], Box<Type>)>,
|
||||
pub(crate) m: kw::m,
|
||||
pub(crate) dot_token: Token![.],
|
||||
pub(crate) memory_fn: MemoryFn,
|
||||
}
|
||||
|
||||
impl ParseTypes<Self> for HdlLetKindMemory {
|
||||
fn parse_types(input: &mut Self, _parser: &mut TypesParser<'_>) -> Result<Self, ParseFailed> {
|
||||
Ok(input.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl_fold! {
|
||||
struct HdlLetKindMemory<> {
|
||||
ty: Option<(Token![:], Box<Type>)>,
|
||||
m: kw::m,
|
||||
dot_token: Token![.],
|
||||
memory_fn: MemoryFn,
|
||||
}
|
||||
}
|
||||
|
@ -679,7 +650,14 @@ impl HdlLetKindToTokens for HdlLetKindMemory {
|
|||
}
|
||||
|
||||
fn expr_to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self { ty: _, memory_fn } = self;
|
||||
let Self {
|
||||
ty: _,
|
||||
m,
|
||||
dot_token,
|
||||
memory_fn,
|
||||
} = self;
|
||||
m.to_tokens(tokens);
|
||||
dot_token.to_tokens(tokens);
|
||||
memory_fn.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
|
@ -689,21 +667,22 @@ impl HdlLetKindMemory {
|
|||
input: ParseStream,
|
||||
parsed_ty: Option<(Token![:], Box<Type>)>,
|
||||
_after_ty: Token![=],
|
||||
m_dot: Option<(kw::m, Token![.])>,
|
||||
m: kw::m,
|
||||
dot_token: Token![.],
|
||||
memory_fn_name: MemoryFnName,
|
||||
) -> syn::Result<Self> {
|
||||
check_empty_m_dot(m_dot, memory_fn_name)?;
|
||||
Ok(Self {
|
||||
ty: parsed_ty,
|
||||
m,
|
||||
dot_token,
|
||||
memory_fn: MemoryFn::parse_rest(input, memory_fn_name)?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) enum HdlLetKind<IOType = ParsedType> {
|
||||
IO(HdlLetKindIO<ModuleIOKind, IOType>),
|
||||
Incomplete(HdlLetKindIncomplete),
|
||||
pub(crate) enum HdlLetKind {
|
||||
IO(HdlLetKindIO),
|
||||
Instance(HdlLetKindInstance),
|
||||
RegBuilder(HdlLetKindRegBuilder),
|
||||
Wire(HdlLetKindWire),
|
||||
|
@ -711,9 +690,8 @@ pub(crate) enum HdlLetKind<IOType = ParsedType> {
|
|||
}
|
||||
|
||||
impl_fold! {
|
||||
enum HdlLetKind<IOType,> {
|
||||
IO(HdlLetKindIO<ModuleIOKind, IOType>),
|
||||
Incomplete(HdlLetKindIncomplete),
|
||||
enum HdlLetKind<> {
|
||||
IO(HdlLetKindIO),
|
||||
Instance(HdlLetKindInstance),
|
||||
RegBuilder(HdlLetKindRegBuilder),
|
||||
Wire(HdlLetKindWire),
|
||||
|
@ -721,30 +699,6 @@ impl_fold! {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: ParseTypes<I>, I> ParseTypes<HdlLetKind<I>> for HdlLetKind<T> {
|
||||
fn parse_types(
|
||||
input: &mut HdlLetKind<I>,
|
||||
parser: &mut TypesParser<'_>,
|
||||
) -> Result<Self, ParseFailed> {
|
||||
match input {
|
||||
HdlLetKind::IO(input) => ParseTypes::parse_types(input, parser).map(HdlLetKind::IO),
|
||||
HdlLetKind::Incomplete(input) => {
|
||||
ParseTypes::parse_types(input, parser).map(HdlLetKind::Incomplete)
|
||||
}
|
||||
HdlLetKind::Instance(input) => {
|
||||
ParseTypes::parse_types(input, parser).map(HdlLetKind::Instance)
|
||||
}
|
||||
HdlLetKind::RegBuilder(input) => {
|
||||
ParseTypes::parse_types(input, parser).map(HdlLetKind::RegBuilder)
|
||||
}
|
||||
HdlLetKind::Wire(input) => ParseTypes::parse_types(input, parser).map(HdlLetKind::Wire),
|
||||
HdlLetKind::Memory(input) => {
|
||||
ParseTypes::parse_types(input, parser).map(HdlLetKind::Memory)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parsed_ty_or_err(
|
||||
parsed_ty: Option<(Token![:], Box<Type>)>,
|
||||
after_ty: Token![=],
|
||||
|
@ -756,15 +710,15 @@ fn parsed_ty_or_err(
|
|||
}
|
||||
}
|
||||
|
||||
impl HdlLetKindIO<ModuleIOKind, Type> {
|
||||
impl HdlLetKindIO {
|
||||
fn rest_of_parse(
|
||||
input: ParseStream,
|
||||
parsed_ty: Option<(Token![:], Box<Type>)>,
|
||||
after_ty: Token![=],
|
||||
m_dot: Option<(kw::m, Token![.])>,
|
||||
m: kw::m,
|
||||
dot_token: Token![.],
|
||||
kind: ModuleIOKind,
|
||||
) -> syn::Result<Self> {
|
||||
let (m, dot_token) = unwrap_m_dot(m_dot, kind)?;
|
||||
let (colon_token, ty) = parsed_ty_or_err(parsed_ty, after_ty)?;
|
||||
let paren_contents;
|
||||
Ok(Self {
|
||||
|
@ -779,36 +733,7 @@ impl HdlLetKindIO<ModuleIOKind, Type> {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_empty_m_dot(m_dot: Option<(kw::m, Token![.])>, kind: impl ToTokens) -> syn::Result<()> {
|
||||
if let Some((m, dot_token)) = m_dot {
|
||||
Err(Error::new_spanned(
|
||||
quote! {#m #dot_token #kind},
|
||||
format_args!(
|
||||
"{} is a free function, not a method of ModuleBuilder: try removing the `m.`",
|
||||
kind.to_token_stream()
|
||||
),
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn unwrap_m_dot(
|
||||
m_dot: Option<(kw::m, Token![.])>,
|
||||
kind: impl ToTokens,
|
||||
) -> syn::Result<(kw::m, Token![.])> {
|
||||
m_dot.ok_or_else(|| {
|
||||
Error::new_spanned(
|
||||
&kind,
|
||||
format_args!(
|
||||
"{} is a ModuleBuilder method, not a free function: try prefixing it with `m.`",
|
||||
kind.to_token_stream()
|
||||
),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
impl HdlLetKindParse for HdlLetKind<Type> {
|
||||
impl HdlLetKindParse for HdlLetKind {
|
||||
type ParsedTy = Option<(Token![:], Box<Type>)>;
|
||||
|
||||
fn parse_ty(input: ParseStream) -> syn::Result<Self::ParsedTy> {
|
||||
|
@ -828,20 +753,16 @@ impl HdlLetKindParse for HdlLetKind<Type> {
|
|||
after_ty: Token![=],
|
||||
input: ParseStream,
|
||||
) -> syn::Result<Self> {
|
||||
let m_dot = if input.peek(kw::m) && input.peek2(Token![.]) {
|
||||
let m = input.parse()?;
|
||||
let dot_token = input.parse()?;
|
||||
Some((m, dot_token))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let m = input.parse()?;
|
||||
let dot_token = input.parse()?;
|
||||
let kind: LetFnKind = input.parse()?;
|
||||
match kind {
|
||||
LetFnKind::Input(input_token) => HdlLetKindIO::rest_of_parse(
|
||||
input,
|
||||
parsed_ty,
|
||||
after_ty,
|
||||
m_dot,
|
||||
m,
|
||||
dot_token,
|
||||
ModuleIOKind::Input(input_token),
|
||||
)
|
||||
.map(Self::IO),
|
||||
|
@ -849,7 +770,8 @@ impl HdlLetKindParse for HdlLetKind<Type> {
|
|||
input,
|
||||
parsed_ty,
|
||||
after_ty,
|
||||
m_dot,
|
||||
m,
|
||||
dot_token,
|
||||
ModuleIOKind::Output(output),
|
||||
)
|
||||
.map(Self::IO),
|
||||
|
@ -860,47 +782,41 @@ impl HdlLetKindParse for HdlLetKind<Type> {
|
|||
"type annotation not allowed for instance",
|
||||
));
|
||||
}
|
||||
check_empty_m_dot(m_dot, kind)?;
|
||||
let paren_contents;
|
||||
Ok(Self::Instance(HdlLetKindInstance {
|
||||
m,
|
||||
dot_token,
|
||||
instance,
|
||||
paren: parenthesized!(paren_contents in input),
|
||||
module: paren_contents.call(parse_single_fn_arg)?,
|
||||
}))
|
||||
}
|
||||
LetFnKind::RegBuilder((reg_builder,)) => {
|
||||
HdlLetKindRegBuilder::rest_of_parse(input, parsed_ty, after_ty, m_dot, reg_builder)
|
||||
.map(Self::RegBuilder)
|
||||
}
|
||||
LetFnKind::RegBuilder((reg_builder,)) => HdlLetKindRegBuilder::rest_of_parse(
|
||||
input,
|
||||
parsed_ty,
|
||||
after_ty,
|
||||
m,
|
||||
dot_token,
|
||||
reg_builder,
|
||||
)
|
||||
.map(Self::RegBuilder),
|
||||
LetFnKind::Wire((wire,)) => {
|
||||
check_empty_m_dot(m_dot, wire)?;
|
||||
let paren_contents;
|
||||
Ok(Self::Wire(HdlLetKindWire {
|
||||
ty: parsed_ty,
|
||||
m,
|
||||
dot_token,
|
||||
wire,
|
||||
paren: parenthesized!(paren_contents in input),
|
||||
ty_expr: paren_contents.call(parse_optional_fn_arg)?,
|
||||
}))
|
||||
}
|
||||
LetFnKind::IncompleteWire(incomplete_wire) => {
|
||||
if let Some(parsed_ty) = parsed_ty {
|
||||
return Err(Error::new_spanned(
|
||||
parsed_ty.1,
|
||||
"type annotation not allowed for incomplete_wire",
|
||||
));
|
||||
}
|
||||
check_empty_m_dot(m_dot, kind)?;
|
||||
let _paren_contents;
|
||||
Ok(Self::Incomplete(HdlLetKindIncomplete {
|
||||
kind: LetFnKindIncomplete::IncompleteWire(incomplete_wire),
|
||||
paren: parenthesized!(_paren_contents in input),
|
||||
}))
|
||||
}
|
||||
LetFnKind::Memory(fn_name) => HdlLetKindMemory::rest_of_parse(
|
||||
input,
|
||||
parsed_ty,
|
||||
after_ty,
|
||||
m_dot,
|
||||
m,
|
||||
dot_token,
|
||||
MemoryFnName::Memory(fn_name),
|
||||
)
|
||||
.map(Self::Memory),
|
||||
|
@ -908,7 +824,8 @@ impl HdlLetKindParse for HdlLetKind<Type> {
|
|||
input,
|
||||
parsed_ty,
|
||||
after_ty,
|
||||
m_dot,
|
||||
m,
|
||||
dot_token,
|
||||
MemoryFnName::MemoryArray(fn_name),
|
||||
)
|
||||
.map(Self::Memory),
|
||||
|
@ -916,7 +833,8 @@ impl HdlLetKindParse for HdlLetKind<Type> {
|
|||
input,
|
||||
parsed_ty,
|
||||
after_ty,
|
||||
m_dot,
|
||||
m,
|
||||
dot_token,
|
||||
MemoryFnName::MemoryWithInit(fn_name),
|
||||
)
|
||||
.map(Self::Memory),
|
||||
|
@ -928,7 +846,6 @@ impl HdlLetKindToTokens for HdlLetKind {
|
|||
fn ty_to_tokens(&self, tokens: &mut TokenStream) {
|
||||
match self {
|
||||
HdlLetKind::IO(v) => v.ty_to_tokens(tokens),
|
||||
HdlLetKind::Incomplete(v) => v.ty_to_tokens(tokens),
|
||||
HdlLetKind::Instance(v) => v.ty_to_tokens(tokens),
|
||||
HdlLetKind::RegBuilder(v) => v.ty_to_tokens(tokens),
|
||||
HdlLetKind::Wire(v) => v.ty_to_tokens(tokens),
|
||||
|
@ -939,7 +856,6 @@ impl HdlLetKindToTokens for HdlLetKind {
|
|||
fn expr_to_tokens(&self, tokens: &mut TokenStream) {
|
||||
match self {
|
||||
HdlLetKind::IO(v) => v.expr_to_tokens(tokens),
|
||||
HdlLetKind::Incomplete(v) => v.expr_to_tokens(tokens),
|
||||
HdlLetKind::Instance(v) => v.expr_to_tokens(tokens),
|
||||
HdlLetKind::RegBuilder(v) => v.expr_to_tokens(tokens),
|
||||
HdlLetKind::Wire(v) => v.expr_to_tokens(tokens),
|
||||
|
@ -952,7 +868,7 @@ with_debug_clone_and_fold! {
|
|||
#[allow(dead_code)]
|
||||
pub(crate) struct HdlLet<Kind = HdlLetKind> {
|
||||
pub(crate) attrs: Vec<Attribute>,
|
||||
pub(crate) hdl_attr: HdlAttr<Nothing, kw::hdl>,
|
||||
pub(crate) hdl_attr: HdlAttr<Nothing>,
|
||||
pub(crate) let_token: Token![let],
|
||||
pub(crate) mut_token: Option<Token![mut]>,
|
||||
pub(crate) name: Ident,
|
||||
|
@ -962,34 +878,6 @@ with_debug_clone_and_fold! {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: ParseTypes<I>, I> ParseTypes<HdlLet<I>> for HdlLet<T> {
|
||||
fn parse_types(
|
||||
input: &mut HdlLet<I>,
|
||||
parser: &mut TypesParser<'_>,
|
||||
) -> Result<Self, ParseFailed> {
|
||||
let HdlLet {
|
||||
attrs,
|
||||
hdl_attr,
|
||||
let_token,
|
||||
mut_token,
|
||||
name,
|
||||
eq_token,
|
||||
kind,
|
||||
semi_token,
|
||||
} = input;
|
||||
Ok(Self {
|
||||
attrs: attrs.clone(),
|
||||
hdl_attr: hdl_attr.clone(),
|
||||
let_token: *let_token,
|
||||
mut_token: *mut_token,
|
||||
name: name.clone(),
|
||||
eq_token: *eq_token,
|
||||
kind: T::parse_types(kind, parser)?,
|
||||
semi_token: *semi_token,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<Kind> HdlLet<Kind> {
|
||||
pub(crate) fn try_map<Kind2, E>(
|
||||
self,
|
||||
|
@ -1118,7 +1006,7 @@ fn wrap_ty_with_expr(ty: impl ToTokens) -> Type {
|
|||
fn unwrap_or_static_type(expr: Option<impl ToTokens>, span: Span) -> TokenStream {
|
||||
expr.map(ToTokens::into_token_stream).unwrap_or_else(|| {
|
||||
quote_spanned! {span=>
|
||||
::fayalite::ty::StaticType::TYPE
|
||||
::fayalite::ty::StaticType::static_type()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -1138,42 +1026,30 @@ impl<T: ToString> ToTokens for ImplicitName<T> {
|
|||
}
|
||||
}
|
||||
|
||||
struct Visitor<'a> {
|
||||
module_kind: Option<ModuleKind>,
|
||||
struct Visitor {
|
||||
module_kind: ModuleKind,
|
||||
errors: Errors,
|
||||
io: Vec<ModuleIO>,
|
||||
block_depth: usize,
|
||||
parsed_generics: &'a ParsedGenerics,
|
||||
}
|
||||
|
||||
impl Visitor<'_> {
|
||||
fn take_hdl_attr<T: Parse>(
|
||||
&mut self,
|
||||
attrs: &mut Vec<Attribute>,
|
||||
) -> Option<HdlAttr<T, kw::hdl>> {
|
||||
impl Visitor {
|
||||
fn take_hdl_attr<T: Parse>(&mut self, attrs: &mut Vec<Attribute>) -> Option<HdlAttr<T>> {
|
||||
self.errors.unwrap_or(
|
||||
HdlAttr::parse_and_take_attr(attrs),
|
||||
Some(syn::parse2::<T>(quote! {}).unwrap().into()),
|
||||
)
|
||||
}
|
||||
fn require_normal_module_or_fn(&mut self, spanned: impl ToTokens) {
|
||||
fn require_normal_module(&mut self, spanned: impl ToTokens) {
|
||||
match self.module_kind {
|
||||
Some(ModuleKind::Extern) => {
|
||||
ModuleKind::Extern => {
|
||||
self.errors
|
||||
.error(spanned, "not allowed in #[hdl_module(extern)]");
|
||||
}
|
||||
Some(ModuleKind::Normal) | None => {}
|
||||
ModuleKind::Normal => {}
|
||||
}
|
||||
}
|
||||
fn require_module(&mut self, spanned: impl ToTokens) {
|
||||
match self.module_kind {
|
||||
None => {
|
||||
self.errors.error(spanned, "not allowed in #[hdl] fn");
|
||||
}
|
||||
Some(_) => {}
|
||||
}
|
||||
}
|
||||
fn process_hdl_if(&mut self, hdl_attr: HdlAttr<Nothing, kw::hdl>, expr_if: ExprIf) -> Expr {
|
||||
fn process_hdl_if(&mut self, hdl_attr: HdlAttr<Nothing>, expr_if: ExprIf) -> Expr {
|
||||
let ExprIf {
|
||||
attrs,
|
||||
if_token,
|
||||
|
@ -1181,7 +1057,7 @@ impl Visitor<'_> {
|
|||
then_branch,
|
||||
else_branch,
|
||||
} = expr_if;
|
||||
self.require_normal_module_or_fn(if_token);
|
||||
self.require_normal_module(if_token);
|
||||
let else_expr = else_branch.unzip().1.map(|else_expr| match *else_expr {
|
||||
Expr::If(expr_if) => self.process_hdl_if(hdl_attr.clone(), expr_if),
|
||||
expr => expr,
|
||||
|
@ -1210,7 +1086,7 @@ impl Visitor<'_> {
|
|||
parse_quote_spanned! {if_token.span=>
|
||||
#(#attrs)*
|
||||
{
|
||||
let mut __scope = ::fayalite::module::if_(#cond);
|
||||
let mut __scope = m.if_(#cond);
|
||||
let _: () = #then_branch;
|
||||
let mut __scope = __scope.else_();
|
||||
let _: () = #else_expr;
|
||||
|
@ -1220,7 +1096,7 @@ impl Visitor<'_> {
|
|||
parse_quote_spanned! {if_token.span=>
|
||||
#(#attrs)*
|
||||
{
|
||||
let mut __scope = ::fayalite::module::if_(#cond);
|
||||
let mut __scope = m.if_(#cond);
|
||||
let _: () = #then_branch;
|
||||
}
|
||||
}
|
||||
|
@ -1246,12 +1122,11 @@ impl Visitor<'_> {
|
|||
.to_tokens(expr);
|
||||
});
|
||||
let mut attrs = hdl_let.attrs.clone();
|
||||
self.require_module(kind);
|
||||
match self.module_kind {
|
||||
Some(ModuleKind::Extern) => attrs.push(parse_quote_spanned! {hdl_let.let_token.span=>
|
||||
ModuleKind::Extern => attrs.push(parse_quote_spanned! {hdl_let.let_token.span=>
|
||||
#[allow(unused_variables)]
|
||||
}),
|
||||
Some(ModuleKind::Normal) | None => {}
|
||||
ModuleKind::Normal => {}
|
||||
}
|
||||
let let_stmt = Local {
|
||||
attrs,
|
||||
|
@ -1282,14 +1157,16 @@ impl Visitor<'_> {
|
|||
eq_token,
|
||||
kind:
|
||||
HdlLetKindInstance {
|
||||
m,
|
||||
dot_token,
|
||||
instance,
|
||||
paren,
|
||||
module,
|
||||
},
|
||||
semi_token,
|
||||
} = hdl_let;
|
||||
self.require_normal_module_or_fn(instance);
|
||||
let mut expr = instance.to_token_stream();
|
||||
self.require_normal_module(instance);
|
||||
let mut expr = quote! {#m #dot_token #instance};
|
||||
paren.surround(&mut expr, |expr| {
|
||||
let name_str = ImplicitName {
|
||||
name: &name,
|
||||
|
@ -1314,9 +1191,11 @@ impl Visitor<'_> {
|
|||
}
|
||||
fn process_hdl_let_reg_builder(&mut self, hdl_let: HdlLet<HdlLetKindRegBuilder>) -> Local {
|
||||
let name = &hdl_let.name;
|
||||
let m = hdl_let.kind.m;
|
||||
let dot = hdl_let.kind.dot_token;
|
||||
let reg_builder = hdl_let.kind.reg_builder;
|
||||
self.require_normal_module_or_fn(reg_builder);
|
||||
let mut expr = reg_builder.to_token_stream();
|
||||
self.require_normal_module(reg_builder);
|
||||
let mut expr = quote! {#m #dot #reg_builder};
|
||||
hdl_let.kind.reg_builder_paren.surround(&mut expr, |expr| {
|
||||
let name_str = ImplicitName {
|
||||
name,
|
||||
|
@ -1340,7 +1219,7 @@ impl Visitor<'_> {
|
|||
no_reset.to_tokens(&mut expr);
|
||||
paren.surround(&mut expr, |expr| ty_expr.to_tokens(expr));
|
||||
}
|
||||
RegBuilderReset::Reset { .. } => {
|
||||
RegBuilderReset::Reset { .. } | RegBuilderReset::ResetDefault { .. } => {
|
||||
hdl_let.kind.reset.to_tokens(&mut expr);
|
||||
}
|
||||
}
|
||||
|
@ -1365,10 +1244,12 @@ impl Visitor<'_> {
|
|||
}
|
||||
fn process_hdl_let_wire(&mut self, hdl_let: HdlLet<HdlLetKindWire>) -> Local {
|
||||
let name = &hdl_let.name;
|
||||
let m = hdl_let.kind.m;
|
||||
let dot = hdl_let.kind.dot_token;
|
||||
let wire = hdl_let.kind.wire;
|
||||
self.require_normal_module_or_fn(wire);
|
||||
self.require_normal_module(wire);
|
||||
let ty_expr = unwrap_or_static_type(hdl_let.kind.ty_expr.as_ref(), wire.span());
|
||||
let mut expr = wire.to_token_stream();
|
||||
let mut expr = quote! {#m #dot #wire};
|
||||
hdl_let.kind.paren.surround(&mut expr, |expr| {
|
||||
let name_str = ImplicitName {
|
||||
name,
|
||||
|
@ -1396,46 +1277,20 @@ impl Visitor<'_> {
|
|||
semi_token: hdl_let.semi_token,
|
||||
}
|
||||
}
|
||||
fn process_hdl_let_incomplete(&mut self, hdl_let: HdlLet<HdlLetKindIncomplete>) -> Local {
|
||||
let name = &hdl_let.name;
|
||||
let kind = hdl_let.kind.kind;
|
||||
self.require_normal_module_or_fn(kind);
|
||||
let mut expr = kind.to_token_stream();
|
||||
hdl_let.kind.paren.surround(&mut expr, |expr| {
|
||||
ImplicitName {
|
||||
name,
|
||||
span: name.span(),
|
||||
}
|
||||
.to_tokens(expr);
|
||||
});
|
||||
let mut_token = &hdl_let.mut_token;
|
||||
Local {
|
||||
attrs: hdl_let.attrs.clone(),
|
||||
let_token: hdl_let.let_token,
|
||||
pat: parse_quote! { #mut_token #name },
|
||||
init: Some(LocalInit {
|
||||
eq_token: hdl_let.eq_token,
|
||||
expr: parse_quote! { #expr },
|
||||
diverge: None,
|
||||
}),
|
||||
semi_token: hdl_let.semi_token,
|
||||
}
|
||||
}
|
||||
fn process_hdl_let_memory(&mut self, hdl_let: HdlLet<HdlLetKindMemory>) -> Local {
|
||||
let name = &hdl_let.name;
|
||||
let m = hdl_let.kind.m;
|
||||
let dot = hdl_let.kind.dot_token;
|
||||
let memory_fn = hdl_let.kind.memory_fn;
|
||||
let memory_fn_name = memory_fn.name();
|
||||
self.require_normal_module_or_fn(memory_fn_name);
|
||||
let mut expr = memory_fn_name.to_token_stream();
|
||||
self.require_normal_module(memory_fn_name);
|
||||
let mut expr = quote! {#m #dot #memory_fn_name};
|
||||
let (paren, arg) = match memory_fn {
|
||||
MemoryFn::Memory {
|
||||
memory,
|
||||
paren,
|
||||
ty_expr,
|
||||
} => (
|
||||
paren,
|
||||
unwrap_or_static_type(ty_expr.as_ref(), memory.span()),
|
||||
),
|
||||
} => (paren, unwrap_or_static_type(ty_expr.as_ref(), memory.span())),
|
||||
MemoryFn::MemoryArray {
|
||||
memory_array,
|
||||
paren,
|
||||
|
@ -1490,7 +1345,6 @@ impl Visitor<'_> {
|
|||
}
|
||||
the_match! {
|
||||
IO => process_hdl_let_io,
|
||||
Incomplete => process_hdl_let_incomplete,
|
||||
Instance => process_hdl_let_instance,
|
||||
RegBuilder => process_hdl_let_reg_builder,
|
||||
Wire => process_hdl_let_wire,
|
||||
|
@ -1523,17 +1377,16 @@ impl Visitor<'_> {
|
|||
let value: BigInt = self
|
||||
.errors
|
||||
.ok(base10_digits.parse().map_err(|e| Error::new(span, e)))?;
|
||||
let bytes = value.to_signed_bytes_le();
|
||||
let path = if signed {
|
||||
known_items::SInt(span).path
|
||||
} else {
|
||||
known_items::UInt(span).path
|
||||
let (negative, bytes) = match value.sign() {
|
||||
Sign::Minus => (true, value.magnitude().to_bytes_le()),
|
||||
Sign::NoSign => (false, vec![]),
|
||||
Sign::Plus => (false, value.magnitude().to_bytes_le()),
|
||||
};
|
||||
Some(parse_quote_spanned! {span=>
|
||||
<#path<#width> as ::fayalite::int::BoolOrIntType>::le_bytes_to_expr_wrapping(
|
||||
::fayalite::int::make_int_literal::<#signed, #width>(
|
||||
#negative,
|
||||
&[#(#bytes,)*],
|
||||
#width,
|
||||
)
|
||||
).to_int_expr()
|
||||
})
|
||||
}
|
||||
fn process_literal(&mut self, literal: ExprLit) -> Expr {
|
||||
|
@ -1596,7 +1449,7 @@ fn empty_let() -> Local {
|
|||
}
|
||||
}
|
||||
|
||||
impl Fold for Visitor<'_> {
|
||||
impl Fold for Visitor {
|
||||
fn fold_item(&mut self, item: Item) -> Item {
|
||||
// don't process item interiors
|
||||
item
|
||||
|
@ -1608,7 +1461,7 @@ impl Fold for Visitor<'_> {
|
|||
}
|
||||
|
||||
fn fold_attribute(&mut self, attr: Attribute) -> Attribute {
|
||||
if is_hdl_attr::<kw::hdl>(&attr) {
|
||||
if is_hdl_attr(&attr) {
|
||||
self.errors
|
||||
.error(&attr, "#[hdl] attribute not supported here");
|
||||
}
|
||||
|
@ -1668,6 +1521,8 @@ impl Fold for Visitor<'_> {
|
|||
Repeat => process_hdl_repeat,
|
||||
Struct => process_hdl_struct,
|
||||
Tuple => process_hdl_tuple,
|
||||
Call => process_hdl_call,
|
||||
Path => process_hdl_path,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1675,23 +1530,17 @@ impl Fold for Visitor<'_> {
|
|||
fn fold_local(&mut self, let_stmt: Local) -> Local {
|
||||
match self
|
||||
.errors
|
||||
.ok(HdlAttr::<Nothing, kw::hdl>::parse_and_leave_attr(
|
||||
&let_stmt.attrs,
|
||||
)) {
|
||||
.ok(HdlAttr::<Nothing>::parse_and_leave_attr(&let_stmt.attrs))
|
||||
{
|
||||
None => return empty_let(),
|
||||
Some(None) => return fold_local(self, let_stmt),
|
||||
Some(Some(HdlAttr { .. })) => {}
|
||||
};
|
||||
let hdl_let = syn::parse2::<HdlLet<HdlLetKind<Type>>>(let_stmt.into_token_stream());
|
||||
let hdl_let = syn::parse2::<HdlLet>(let_stmt.into_token_stream());
|
||||
let Some(hdl_let) = self.errors.ok(hdl_let) else {
|
||||
return empty_let();
|
||||
};
|
||||
let mut hdl_let = hdl_let.do_fold(self);
|
||||
let Ok(hdl_let) =
|
||||
TypesParser::run_with_errors(self.parsed_generics, &mut hdl_let, &mut self.errors)
|
||||
else {
|
||||
return empty_let();
|
||||
};
|
||||
let hdl_let = hdl_let.do_fold(self);
|
||||
self.process_hdl_let(hdl_let)
|
||||
}
|
||||
|
||||
|
@ -1712,16 +1561,14 @@ impl Fold for Visitor<'_> {
|
|||
}
|
||||
|
||||
pub(crate) fn transform_body(
|
||||
module_kind: Option<ModuleKind>,
|
||||
module_kind: ModuleKind,
|
||||
mut body: Box<Block>,
|
||||
parsed_generics: &ParsedGenerics,
|
||||
) -> syn::Result<(Box<Block>, Vec<ModuleIO>)> {
|
||||
let mut visitor = Visitor {
|
||||
module_kind,
|
||||
errors: Errors::new(),
|
||||
io: vec![],
|
||||
block_depth: 0,
|
||||
parsed_generics,
|
||||
};
|
||||
*body = syn::fold::fold_block(&mut visitor, *body);
|
||||
visitor.errors.finish()?;
|
||||
|
|
|
@ -1,19 +1,342 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use crate::{kw, module::transform_body::Visitor, HdlAttr};
|
||||
use quote::{format_ident, quote_spanned};
|
||||
use crate::{module::transform_body::Visitor, options, Errors, HdlAttr, PairsIterExt};
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::{format_ident, quote_spanned, ToTokens, TokenStreamExt};
|
||||
use syn::{
|
||||
parse::Nothing, parse_quote, parse_quote_spanned, spanned::Spanned, Expr, ExprArray, ExprPath,
|
||||
ExprRepeat, ExprStruct, ExprTuple, FieldValue, TypePath,
|
||||
parse::Nothing,
|
||||
parse_quote, parse_quote_spanned,
|
||||
punctuated::{Pair, Punctuated},
|
||||
spanned::Spanned,
|
||||
token::{Brace, Paren},
|
||||
Attribute, Expr, ExprArray, ExprCall, ExprGroup, ExprPath, ExprRepeat, ExprStruct, ExprTuple,
|
||||
FieldValue, Ident, Index, Member, Path, PathArguments, PathSegment, Token, TypePath,
|
||||
};
|
||||
|
||||
impl Visitor<'_> {
|
||||
options! {
|
||||
#[options = AggregateLiteralOptions]
|
||||
#[no_ident_fragment]
|
||||
pub(crate) enum AggregateLiteralOption {
|
||||
Struct(struct_),
|
||||
Enum(enum_),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct StructOrEnumPath {
|
||||
pub(crate) ty: TypePath,
|
||||
pub(crate) variant: Option<(TypePath, Ident)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub(crate) struct SingleSegmentVariant {
|
||||
pub(crate) name: &'static str,
|
||||
pub(crate) make_type_path: fn(Span, &PathArguments) -> Path,
|
||||
}
|
||||
|
||||
impl StructOrEnumPath {
|
||||
pub(crate) const SINGLE_SEGMENT_VARIANTS: &'static [SingleSegmentVariant] = {
|
||||
fn make_option_type_path(span: Span, arguments: &PathArguments) -> Path {
|
||||
let arguments = if arguments.is_none() {
|
||||
quote_spanned! {span=>
|
||||
<_>
|
||||
}
|
||||
} else {
|
||||
arguments.to_token_stream()
|
||||
};
|
||||
parse_quote_spanned! {span=>
|
||||
::fayalite::__std::option::Option #arguments
|
||||
}
|
||||
}
|
||||
fn make_result_type_path(span: Span, arguments: &PathArguments) -> Path {
|
||||
let arguments = if arguments.is_none() {
|
||||
quote_spanned! {span=>
|
||||
<_, _>
|
||||
}
|
||||
} else {
|
||||
arguments.to_token_stream()
|
||||
};
|
||||
parse_quote_spanned! {span=>
|
||||
::fayalite::__std::result::Result #arguments
|
||||
}
|
||||
}
|
||||
&[
|
||||
SingleSegmentVariant {
|
||||
name: "Some",
|
||||
make_type_path: make_option_type_path,
|
||||
},
|
||||
SingleSegmentVariant {
|
||||
name: "None",
|
||||
make_type_path: make_option_type_path,
|
||||
},
|
||||
SingleSegmentVariant {
|
||||
name: "Ok",
|
||||
make_type_path: make_result_type_path,
|
||||
},
|
||||
SingleSegmentVariant {
|
||||
name: "Err",
|
||||
make_type_path: make_result_type_path,
|
||||
},
|
||||
]
|
||||
};
|
||||
pub(crate) fn new(
|
||||
errors: &mut Errors,
|
||||
path: TypePath,
|
||||
options: &AggregateLiteralOptions,
|
||||
) -> Result<Self, ()> {
|
||||
let Path {
|
||||
leading_colon,
|
||||
segments,
|
||||
} = &path.path;
|
||||
let qself_position = path.qself.as_ref().map(|qself| qself.position).unwrap_or(0);
|
||||
let variant_name = if qself_position < segments.len() {
|
||||
Some(segments.last().unwrap().ident.clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let enum_type = 'guess_enum_type: {
|
||||
if options.enum_.is_some() {
|
||||
if let Some((struct_,)) = options.struct_ {
|
||||
errors.error(
|
||||
struct_,
|
||||
"can't specify both #[hdl(enum)] and #[hdl(struct)]",
|
||||
);
|
||||
}
|
||||
break 'guess_enum_type Some(None);
|
||||
}
|
||||
if options.struct_.is_some() {
|
||||
break 'guess_enum_type None;
|
||||
}
|
||||
if path.qself.is_none() && leading_colon.is_none() && segments.len() == 1 {
|
||||
let PathSegment { ident, arguments } = &segments[0];
|
||||
for &SingleSegmentVariant {
|
||||
name,
|
||||
make_type_path,
|
||||
} in Self::SINGLE_SEGMENT_VARIANTS
|
||||
{
|
||||
if ident == name {
|
||||
break 'guess_enum_type Some(Some(TypePath {
|
||||
qself: None,
|
||||
path: make_type_path(ident.span(), arguments),
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
if segments.len() == qself_position + 2
|
||||
&& segments[qself_position + 1].arguments.is_none()
|
||||
&& (path.qself.is_some()
|
||||
|| segments[qself_position].ident.to_string().as_bytes()[0]
|
||||
.is_ascii_uppercase())
|
||||
{
|
||||
let mut ty = path.clone();
|
||||
ty.path.segments.pop();
|
||||
ty.path.segments.pop_punct();
|
||||
break 'guess_enum_type Some(Some(ty));
|
||||
}
|
||||
None
|
||||
};
|
||||
if let Some(enum_type) = enum_type {
|
||||
let ty = if let Some(enum_type) = enum_type {
|
||||
enum_type
|
||||
} else {
|
||||
if qself_position >= segments.len() {
|
||||
errors.error(path, "#[hdl]: can't figure out enum's type");
|
||||
return Err(());
|
||||
}
|
||||
let mut ty = path.clone();
|
||||
ty.path.segments.pop();
|
||||
ty.path.segments.pop_punct();
|
||||
ty
|
||||
};
|
||||
let Some(variant_name) = variant_name else {
|
||||
errors.error(path, "#[hdl]: can't figure out enum's variant name");
|
||||
return Err(());
|
||||
};
|
||||
Ok(Self {
|
||||
ty,
|
||||
variant: Some((path, variant_name)),
|
||||
})
|
||||
} else {
|
||||
Ok(Self {
|
||||
ty: path,
|
||||
variant: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub(crate) enum BraceOrParen {
|
||||
Brace(Brace),
|
||||
Paren(Paren),
|
||||
}
|
||||
|
||||
impl BraceOrParen {
|
||||
pub(crate) fn surround(self, tokens: &mut TokenStream, f: impl FnOnce(&mut TokenStream)) {
|
||||
match self {
|
||||
BraceOrParen::Brace(v) => v.surround(tokens, f),
|
||||
BraceOrParen::Paren(v) => v.surround(tokens, f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct StructOrEnumLiteralField {
|
||||
pub(crate) attrs: Vec<Attribute>,
|
||||
pub(crate) member: Member,
|
||||
pub(crate) colon_token: Option<Token![:]>,
|
||||
pub(crate) expr: Expr,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct StructOrEnumLiteral {
|
||||
pub(crate) attrs: Vec<Attribute>,
|
||||
pub(crate) path: TypePath,
|
||||
pub(crate) brace_or_paren: BraceOrParen,
|
||||
pub(crate) fields: Punctuated<StructOrEnumLiteralField, Token![,]>,
|
||||
pub(crate) dot2_token: Option<Token![..]>,
|
||||
pub(crate) rest: Option<Box<Expr>>,
|
||||
}
|
||||
|
||||
impl StructOrEnumLiteral {
|
||||
pub(crate) fn map_field_exprs(self, mut f: impl FnMut(Expr) -> Expr) -> Self {
|
||||
self.map_fields(|mut field| {
|
||||
field.expr = f(field.expr);
|
||||
field
|
||||
})
|
||||
}
|
||||
pub(crate) fn map_fields(
|
||||
self,
|
||||
f: impl FnMut(StructOrEnumLiteralField) -> StructOrEnumLiteralField,
|
||||
) -> Self {
|
||||
let Self {
|
||||
attrs,
|
||||
path,
|
||||
brace_or_paren,
|
||||
fields,
|
||||
dot2_token,
|
||||
rest,
|
||||
} = self;
|
||||
let fields = fields.into_pairs().map_pair_value(f).collect();
|
||||
Self {
|
||||
attrs,
|
||||
path,
|
||||
brace_or_paren,
|
||||
fields,
|
||||
dot2_token,
|
||||
rest,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ExprStruct> for StructOrEnumLiteral {
|
||||
fn from(value: ExprStruct) -> Self {
|
||||
let ExprStruct {
|
||||
attrs,
|
||||
qself,
|
||||
path,
|
||||
brace_token,
|
||||
fields,
|
||||
dot2_token,
|
||||
rest,
|
||||
} = value;
|
||||
Self {
|
||||
attrs,
|
||||
path: TypePath { qself, path },
|
||||
brace_or_paren: BraceOrParen::Brace(brace_token),
|
||||
fields: fields
|
||||
.into_pairs()
|
||||
.map_pair_value(
|
||||
|FieldValue {
|
||||
attrs,
|
||||
member,
|
||||
colon_token,
|
||||
expr,
|
||||
}| StructOrEnumLiteralField {
|
||||
attrs,
|
||||
member,
|
||||
colon_token,
|
||||
expr,
|
||||
},
|
||||
)
|
||||
.collect(),
|
||||
dot2_token,
|
||||
rest,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn expr_to_member(expr: &Expr) -> Option<Member> {
|
||||
syn::parse2(expr.to_token_stream()).ok()
|
||||
}
|
||||
|
||||
impl ToTokens for StructOrEnumLiteral {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self {
|
||||
attrs,
|
||||
path,
|
||||
brace_or_paren,
|
||||
fields,
|
||||
dot2_token,
|
||||
rest,
|
||||
} = self;
|
||||
tokens.append_all(attrs);
|
||||
path.to_tokens(tokens);
|
||||
brace_or_paren.surround(tokens, |tokens| {
|
||||
match brace_or_paren {
|
||||
BraceOrParen::Brace(_) => {
|
||||
for (
|
||||
StructOrEnumLiteralField {
|
||||
attrs,
|
||||
member,
|
||||
mut colon_token,
|
||||
expr,
|
||||
},
|
||||
comma,
|
||||
) in fields.pairs().map(|v| v.into_tuple())
|
||||
{
|
||||
tokens.append_all(attrs);
|
||||
if Some(member) != expr_to_member(expr).as_ref() {
|
||||
colon_token = Some(<Token![:]>::default());
|
||||
}
|
||||
member.to_tokens(tokens);
|
||||
colon_token.to_tokens(tokens);
|
||||
expr.to_tokens(tokens);
|
||||
comma.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
BraceOrParen::Paren(_) => {
|
||||
for (
|
||||
StructOrEnumLiteralField {
|
||||
attrs,
|
||||
member: _,
|
||||
colon_token: _,
|
||||
expr,
|
||||
},
|
||||
comma,
|
||||
) in fields.pairs().map(|v| v.into_tuple())
|
||||
{
|
||||
tokens.append_all(attrs);
|
||||
expr.to_tokens(tokens);
|
||||
comma.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(rest) = rest {
|
||||
dot2_token.unwrap_or_default().to_tokens(tokens);
|
||||
rest.to_tokens(tokens);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl Visitor {
|
||||
pub(crate) fn process_hdl_array(
|
||||
&mut self,
|
||||
hdl_attr: HdlAttr<Nothing, kw::hdl>,
|
||||
hdl_attr: HdlAttr<Nothing>,
|
||||
mut expr_array: ExprArray,
|
||||
) -> Expr {
|
||||
self.require_normal_module_or_fn(hdl_attr);
|
||||
self.require_normal_module(hdl_attr);
|
||||
for elem in &mut expr_array.elems {
|
||||
*elem = parse_quote_spanned! {elem.span()=>
|
||||
::fayalite::expr::ToExpr::to_expr(&(#elem))
|
||||
|
@ -23,80 +346,195 @@ impl Visitor<'_> {
|
|||
}
|
||||
pub(crate) fn process_hdl_repeat(
|
||||
&mut self,
|
||||
hdl_attr: HdlAttr<Nothing, kw::hdl>,
|
||||
hdl_attr: HdlAttr<Nothing>,
|
||||
mut expr_repeat: ExprRepeat,
|
||||
) -> Expr {
|
||||
self.require_normal_module_or_fn(hdl_attr);
|
||||
self.require_normal_module(hdl_attr);
|
||||
let repeated_value = &expr_repeat.expr;
|
||||
*expr_repeat.expr = parse_quote_spanned! {repeated_value.span()=>
|
||||
::fayalite::expr::ToExpr::to_expr(&(#repeated_value))
|
||||
};
|
||||
parse_quote! {::fayalite::expr::ToExpr::to_expr(&#expr_repeat)}
|
||||
}
|
||||
pub(crate) fn process_hdl_struct(
|
||||
pub(crate) fn process_struct_enum(
|
||||
&mut self,
|
||||
hdl_attr: HdlAttr<Nothing, kw::hdl>,
|
||||
expr_struct: ExprStruct,
|
||||
hdl_attr: HdlAttr<AggregateLiteralOptions>,
|
||||
mut literal: StructOrEnumLiteral,
|
||||
) -> Expr {
|
||||
self.require_normal_module_or_fn(&hdl_attr);
|
||||
let name_span = expr_struct.path.segments.last().unwrap().ident.span();
|
||||
let builder_ident = format_ident!("__builder", span = name_span);
|
||||
let empty_builder = if expr_struct.qself.is_some()
|
||||
|| expr_struct
|
||||
.path
|
||||
.segments
|
||||
.iter()
|
||||
.any(|seg| !seg.arguments.is_none())
|
||||
let span = hdl_attr.hdl.span;
|
||||
if let Some(rest) = literal.rest.take() {
|
||||
self.errors
|
||||
.error(rest, "#[hdl] struct functional update syntax not supported");
|
||||
}
|
||||
let mut next_var = 0usize;
|
||||
let mut new_var = || -> Ident {
|
||||
let retval = format_ident!("__v{}", next_var, span = span);
|
||||
next_var += 1;
|
||||
retval
|
||||
};
|
||||
let infallible_var = new_var();
|
||||
let retval_var = new_var();
|
||||
let mut lets = vec![];
|
||||
let mut build_steps = vec![];
|
||||
let literal = literal.map_field_exprs(|expr| {
|
||||
let field_var = new_var();
|
||||
lets.push(quote_spanned! {span=>
|
||||
let #field_var = ::fayalite::expr::ToExpr::to_expr(&#expr);
|
||||
});
|
||||
parse_quote! { #field_var }
|
||||
});
|
||||
let Ok(StructOrEnumPath { ty, variant }) =
|
||||
StructOrEnumPath::new(&mut self.errors, literal.path.clone(), &hdl_attr.body)
|
||||
else {
|
||||
return parse_quote_spanned! {span=>
|
||||
{}
|
||||
};
|
||||
};
|
||||
for StructOrEnumLiteralField {
|
||||
attrs: _,
|
||||
member,
|
||||
colon_token: _,
|
||||
expr,
|
||||
} in literal.fields.iter()
|
||||
{
|
||||
let ty = TypePath {
|
||||
qself: expr_struct.qself,
|
||||
path: expr_struct.path,
|
||||
};
|
||||
let builder_ty = quote_spanned! {name_span=>
|
||||
<#ty as ::fayalite::bundle::BundleType>::Builder
|
||||
};
|
||||
quote_spanned! {name_span=>
|
||||
<#builder_ty as ::fayalite::__std::default::Default>::default()
|
||||
let field_fn = format_ident!("field_{}", member);
|
||||
build_steps.push(quote_spanned! {span=>
|
||||
let #retval_var = #retval_var.#field_fn(#expr);
|
||||
});
|
||||
}
|
||||
let check_literal = literal.map_field_exprs(|expr| {
|
||||
parse_quote_spanned! {span=>
|
||||
::fayalite::expr::value_from_expr_type(#expr, #infallible_var)
|
||||
}
|
||||
});
|
||||
let make_expr_fn = if let Some((_variant_path, variant_ident)) = &variant {
|
||||
let variant_fn = format_ident!("variant_{}", variant_ident);
|
||||
build_steps.push(quote_spanned! {span=>
|
||||
let #retval_var = #retval_var.#variant_fn();
|
||||
});
|
||||
quote_spanned! {span=>
|
||||
::fayalite::expr::make_enum_expr
|
||||
}
|
||||
} else {
|
||||
let path = ExprPath {
|
||||
attrs: vec![],
|
||||
qself: expr_struct.qself,
|
||||
path: expr_struct.path,
|
||||
};
|
||||
quote_spanned! {name_span=>
|
||||
#path::__bundle_builder()
|
||||
build_steps.push(quote_spanned! {span=>
|
||||
let #retval_var = #retval_var.build();
|
||||
});
|
||||
quote_spanned! {span=>
|
||||
::fayalite::expr::make_bundle_expr
|
||||
}
|
||||
};
|
||||
let field_calls = Vec::from_iter(expr_struct.fields.iter().map(
|
||||
|FieldValue {
|
||||
attrs: _,
|
||||
member,
|
||||
colon_token: _,
|
||||
expr,
|
||||
}| {
|
||||
let field_fn = format_ident!("field_{}", member);
|
||||
quote_spanned! {member.span()=>
|
||||
let #builder_ident = #builder_ident.#field_fn(#expr);
|
||||
}
|
||||
},
|
||||
));
|
||||
parse_quote_spanned! {name_span=>
|
||||
let variant_or_type =
|
||||
variant.map_or_else(|| ty.clone(), |(variant_path, _variant_ident)| variant_path);
|
||||
parse_quote_spanned! {span=>
|
||||
{
|
||||
let #builder_ident = #empty_builder;
|
||||
#(#field_calls)*
|
||||
::fayalite::expr::ToExpr::to_expr(&#builder_ident)
|
||||
#(#lets)*
|
||||
#make_expr_fn::<#ty>(|#infallible_var| {
|
||||
let #retval_var = #check_literal;
|
||||
#[allow(unreachable_code)]
|
||||
match #retval_var {
|
||||
#variant_or_type { .. } => #retval_var,
|
||||
#[allow(unreachable_patterns)]
|
||||
_ => match #infallible_var {},
|
||||
}
|
||||
}, |#retval_var| {
|
||||
#(#build_steps)*
|
||||
#retval_var
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
pub(crate) fn process_hdl_struct(
|
||||
&mut self,
|
||||
hdl_attr: HdlAttr<AggregateLiteralOptions>,
|
||||
expr_struct: ExprStruct,
|
||||
) -> Expr {
|
||||
self.require_normal_module(&hdl_attr);
|
||||
self.process_struct_enum(hdl_attr, expr_struct.into())
|
||||
}
|
||||
pub(crate) fn process_hdl_tuple(
|
||||
&mut self,
|
||||
hdl_attr: HdlAttr<Nothing, kw::hdl>,
|
||||
hdl_attr: HdlAttr<Nothing>,
|
||||
expr_tuple: ExprTuple,
|
||||
) -> Expr {
|
||||
self.require_normal_module_or_fn(hdl_attr);
|
||||
self.require_normal_module(hdl_attr);
|
||||
parse_quote_spanned! {expr_tuple.span()=>
|
||||
::fayalite::expr::ToExpr::to_expr(&#expr_tuple)
|
||||
}
|
||||
}
|
||||
pub(crate) fn process_hdl_path(
|
||||
&mut self,
|
||||
hdl_attr: HdlAttr<Nothing>,
|
||||
expr_path: ExprPath,
|
||||
) -> Expr {
|
||||
self.require_normal_module(hdl_attr);
|
||||
parse_quote_spanned! {expr_path.span()=>
|
||||
::fayalite::expr::ToExpr::to_expr(&#expr_path)
|
||||
}
|
||||
}
|
||||
pub(crate) fn process_hdl_call(
|
||||
&mut self,
|
||||
hdl_attr: HdlAttr<AggregateLiteralOptions>,
|
||||
expr_call: ExprCall,
|
||||
) -> Expr {
|
||||
self.require_normal_module(&hdl_attr);
|
||||
let ExprCall {
|
||||
attrs: mut literal_attrs,
|
||||
func,
|
||||
paren_token,
|
||||
args,
|
||||
} = expr_call;
|
||||
let mut path_expr = *func;
|
||||
let path = loop {
|
||||
break match path_expr {
|
||||
Expr::Group(ExprGroup {
|
||||
attrs,
|
||||
group_token: _,
|
||||
expr,
|
||||
}) => {
|
||||
literal_attrs.extend(attrs);
|
||||
path_expr = *expr;
|
||||
continue;
|
||||
}
|
||||
Expr::Path(ExprPath { attrs, qself, path }) => {
|
||||
literal_attrs.extend(attrs);
|
||||
TypePath { qself, path }
|
||||
}
|
||||
_ => {
|
||||
self.errors.error(&path_expr, "missing tuple struct's name");
|
||||
return parse_quote_spanned! {path_expr.span()=>
|
||||
{}
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
let fields = args
|
||||
.into_pairs()
|
||||
.enumerate()
|
||||
.map(|(index, p)| {
|
||||
let (expr, comma) = p.into_tuple();
|
||||
let mut index = Index::from(index);
|
||||
index.span = hdl_attr.hdl.span;
|
||||
Pair::new(
|
||||
StructOrEnumLiteralField {
|
||||
attrs: vec![],
|
||||
member: Member::Unnamed(index),
|
||||
colon_token: None,
|
||||
expr,
|
||||
},
|
||||
comma,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
self.process_struct_enum(
|
||||
hdl_attr,
|
||||
StructOrEnumLiteral {
|
||||
attrs: literal_attrs,
|
||||
path,
|
||||
brace_or_paren: BraceOrParen::Paren(paren_token),
|
||||
fields,
|
||||
dot2_token: None,
|
||||
rest: None,
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,22 +1,24 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
fold::{impl_fold, DoFold},
|
||||
kw,
|
||||
module::transform_body::{with_debug_clone_and_fold, Visitor},
|
||||
fold::impl_fold,
|
||||
module::transform_body::{
|
||||
expand_aggregate_literals::{AggregateLiteralOptions, StructOrEnumPath},
|
||||
with_debug_clone_and_fold, Visitor,
|
||||
},
|
||||
Errors, HdlAttr, PairsIterExt,
|
||||
};
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::{format_ident, quote_spanned, ToTokens, TokenStreamExt};
|
||||
use quote::{ToTokens, TokenStreamExt};
|
||||
use syn::{
|
||||
fold::{fold_arm, fold_expr_match, fold_pat, Fold},
|
||||
parse::Nothing,
|
||||
parse_quote_spanned,
|
||||
punctuated::Punctuated,
|
||||
punctuated::{Pair, Punctuated},
|
||||
spanned::Spanned,
|
||||
token::{Brace, Paren},
|
||||
Arm, Attribute, Expr, ExprMatch, FieldPat, Ident, Member, Pat, PatIdent, PatOr, PatParen,
|
||||
PatPath, PatRest, PatStruct, PatTupleStruct, PatWild, Path, PathSegment, Token, TypePath,
|
||||
Arm, Attribute, Expr, ExprMatch, FieldPat, Ident, Index, Member, Pat, PatIdent, PatOr,
|
||||
PatParen, PatPath, PatRest, PatStruct, PatTupleStruct, PatWild, Path, Token, TypePath,
|
||||
};
|
||||
|
||||
with_debug_clone_and_fold! {
|
||||
|
@ -79,7 +81,7 @@ impl ToTokens for MatchPatWild {
|
|||
|
||||
with_debug_clone_and_fold! {
|
||||
struct MatchPatStructField<> {
|
||||
field_name: Ident,
|
||||
member: Member,
|
||||
colon_token: Option<Token![:]>,
|
||||
pat: MatchPatSimple,
|
||||
}
|
||||
|
@ -88,19 +90,12 @@ with_debug_clone_and_fold! {
|
|||
impl ToTokens for MatchPatStructField {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self {
|
||||
field_name,
|
||||
member,
|
||||
colon_token,
|
||||
pat,
|
||||
} = self;
|
||||
field_name.to_tokens(tokens);
|
||||
if let (None, MatchPatSimple::Binding(MatchPatBinding { ident })) = (colon_token, pat) {
|
||||
if field_name == ident {
|
||||
return;
|
||||
}
|
||||
}
|
||||
colon_token
|
||||
.unwrap_or_else(|| Token![:](field_name.span()))
|
||||
.to_tokens(tokens);
|
||||
member.to_tokens(tokens);
|
||||
colon_token.to_tokens(tokens);
|
||||
pat.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
|
@ -113,16 +108,8 @@ impl MatchPatStructField {
|
|||
colon_token,
|
||||
pat,
|
||||
} = field_pat;
|
||||
let field_name = if let Member::Named(field_name) = member {
|
||||
field_name
|
||||
} else {
|
||||
state
|
||||
.errors
|
||||
.error(&member, "field name must not be a number");
|
||||
format_ident!("_{}", member)
|
||||
};
|
||||
Ok(Self {
|
||||
field_name,
|
||||
member,
|
||||
colon_token,
|
||||
pat: MatchPatSimple::parse(state, *pat)?,
|
||||
})
|
||||
|
@ -131,8 +118,7 @@ impl MatchPatStructField {
|
|||
|
||||
with_debug_clone_and_fold! {
|
||||
struct MatchPatStruct<> {
|
||||
match_span: Span,
|
||||
path: Path,
|
||||
resolved_path: Path,
|
||||
brace_token: Brace,
|
||||
fields: Punctuated<MatchPatStructField, Token![,]>,
|
||||
rest: Option<Token![..]>,
|
||||
|
@ -142,16 +128,12 @@ with_debug_clone_and_fold! {
|
|||
impl ToTokens for MatchPatStruct {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self {
|
||||
match_span,
|
||||
path,
|
||||
resolved_path,
|
||||
brace_token,
|
||||
fields,
|
||||
rest,
|
||||
} = self;
|
||||
quote_spanned! {*match_span=>
|
||||
__MatchTy::<#path>
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
resolved_path.to_tokens(tokens);
|
||||
brace_token.surround(tokens, |tokens| {
|
||||
fields.to_tokens(tokens);
|
||||
rest.to_tokens(tokens);
|
||||
|
@ -159,35 +141,6 @@ impl ToTokens for MatchPatStruct {
|
|||
}
|
||||
}
|
||||
|
||||
with_debug_clone_and_fold! {
|
||||
struct MatchPatEnumVariant<> {
|
||||
match_span: Span,
|
||||
variant_path: Path,
|
||||
enum_path: Path,
|
||||
variant_name: Ident,
|
||||
field: Option<(Paren, MatchPatSimple)>,
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for MatchPatEnumVariant {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self {
|
||||
match_span,
|
||||
variant_path: _,
|
||||
enum_path,
|
||||
variant_name,
|
||||
field,
|
||||
} = self;
|
||||
quote_spanned! {*match_span=>
|
||||
__MatchTy::<#enum_path>::#variant_name
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
if let Some((paren_token, field)) = field {
|
||||
paren_token.surround(tokens, |tokens| field.to_tokens(tokens));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
enum MatchPatSimple {
|
||||
Paren(MatchPatParen<MatchPatSimple>),
|
||||
|
@ -216,70 +169,21 @@ impl ToTokens for MatchPatSimple {
|
|||
}
|
||||
}
|
||||
|
||||
struct EnumPath {
|
||||
variant_path: Path,
|
||||
enum_path: Path,
|
||||
variant_name: Ident,
|
||||
}
|
||||
|
||||
fn parse_enum_path(variant_path: TypePath) -> Result<EnumPath, TypePath> {
|
||||
let TypePath {
|
||||
qself: None,
|
||||
path: variant_path,
|
||||
} = variant_path
|
||||
else {
|
||||
return Err(variant_path);
|
||||
};
|
||||
if variant_path.is_ident("HdlNone") || variant_path.is_ident("HdlSome") {
|
||||
let ident = variant_path.get_ident().unwrap();
|
||||
return Ok(EnumPath {
|
||||
enum_path: parse_quote_spanned! {ident.span()=>
|
||||
::fayalite::enum_::HdlOption::<_>
|
||||
},
|
||||
variant_name: ident.clone(),
|
||||
variant_path,
|
||||
});
|
||||
}
|
||||
if variant_path.segments.len() < 2 {
|
||||
return Err(TypePath {
|
||||
qself: None,
|
||||
path: variant_path,
|
||||
});
|
||||
}
|
||||
let mut enum_path = variant_path.clone();
|
||||
let PathSegment {
|
||||
ident: variant_name,
|
||||
arguments,
|
||||
} = enum_path.segments.pop().unwrap().into_value();
|
||||
if !arguments.is_none() {
|
||||
return Err(TypePath {
|
||||
qself: None,
|
||||
path: variant_path,
|
||||
});
|
||||
}
|
||||
enum_path.segments.pop_punct();
|
||||
Ok(EnumPath {
|
||||
variant_path,
|
||||
enum_path,
|
||||
variant_name,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_enum_ident(ident: Ident) -> Result<EnumPath, Ident> {
|
||||
parse_enum_path(TypePath {
|
||||
qself: None,
|
||||
path: ident.into(),
|
||||
})
|
||||
.map_err(|p| p.path.segments.into_iter().next().unwrap().ident)
|
||||
fn is_pat_ident_a_struct_or_enum_name(ident: &Ident) -> bool {
|
||||
ident
|
||||
.to_string()
|
||||
.starts_with(|ch: char| ch.is_ascii_uppercase())
|
||||
}
|
||||
|
||||
trait ParseMatchPat: Sized {
|
||||
fn simple(v: MatchPatSimple) -> Self;
|
||||
fn or(v: MatchPatOr<Self>) -> Self;
|
||||
fn paren(v: MatchPatParen<Self>) -> Self;
|
||||
fn struct_(state: &mut HdlMatchParseState<'_>, v: MatchPatStruct) -> Result<Self, ()>;
|
||||
fn enum_variant(state: &mut HdlMatchParseState<'_>, v: MatchPatEnumVariant)
|
||||
-> Result<Self, ()>;
|
||||
fn struct_(
|
||||
state: &mut HdlMatchParseState<'_>,
|
||||
v: MatchPatStruct,
|
||||
struct_error_spanned: &dyn ToTokens,
|
||||
) -> Result<Self, ()>;
|
||||
fn parse(state: &mut HdlMatchParseState<'_>, pat: Pat) -> Result<Self, ()> {
|
||||
match pat {
|
||||
Pat::Ident(PatIdent {
|
||||
|
@ -304,24 +208,26 @@ trait ParseMatchPat: Sized {
|
|||
.errors
|
||||
.error(at_token, "@ not allowed in #[hdl] patterns");
|
||||
}
|
||||
match parse_enum_ident(ident) {
|
||||
Ok(EnumPath {
|
||||
variant_path,
|
||||
enum_path,
|
||||
variant_name,
|
||||
}) => Self::enum_variant(
|
||||
if is_pat_ident_a_struct_or_enum_name(&ident) {
|
||||
let ident_span = ident.span();
|
||||
let resolved_path = state.resolve_enum_struct_path(TypePath {
|
||||
qself: None,
|
||||
path: ident.clone().into(),
|
||||
})?;
|
||||
Self::struct_(
|
||||
state,
|
||||
MatchPatEnumVariant {
|
||||
match_span: state.match_span,
|
||||
variant_path,
|
||||
enum_path,
|
||||
variant_name,
|
||||
field: None,
|
||||
MatchPatStruct {
|
||||
resolved_path,
|
||||
brace_token: Brace(ident_span),
|
||||
fields: Punctuated::new(),
|
||||
rest: None,
|
||||
},
|
||||
),
|
||||
Err(ident) => Ok(Self::simple(MatchPatSimple::Binding(MatchPatBinding {
|
||||
&ident,
|
||||
)
|
||||
} else {
|
||||
Ok(Self::simple(MatchPatSimple::Binding(MatchPatBinding {
|
||||
ident,
|
||||
}))),
|
||||
})))
|
||||
}
|
||||
}
|
||||
Pat::Or(PatOr {
|
||||
|
@ -348,22 +254,18 @@ trait ParseMatchPat: Sized {
|
|||
qself,
|
||||
path,
|
||||
}) => {
|
||||
let EnumPath {
|
||||
variant_path,
|
||||
enum_path,
|
||||
variant_name,
|
||||
} = parse_enum_path(TypePath { qself, path }).map_err(|path| {
|
||||
state.errors.error(path, "unsupported enum variant path");
|
||||
})?;
|
||||
Self::enum_variant(
|
||||
let path = TypePath { qself, path };
|
||||
let path_span = path.span();
|
||||
let resolved_path = state.resolve_enum_struct_path(path.clone())?;
|
||||
Self::struct_(
|
||||
state,
|
||||
MatchPatEnumVariant {
|
||||
match_span: state.match_span,
|
||||
variant_path,
|
||||
enum_path,
|
||||
variant_name,
|
||||
field: None,
|
||||
MatchPatStruct {
|
||||
resolved_path,
|
||||
brace_token: Brace(path_span),
|
||||
fields: Punctuated::new(),
|
||||
rest: None,
|
||||
},
|
||||
&path,
|
||||
)
|
||||
}
|
||||
Pat::Struct(PatStruct {
|
||||
|
@ -380,17 +282,12 @@ trait ParseMatchPat: Sized {
|
|||
MatchPatStructField::parse(state, field_pat).ok()
|
||||
})
|
||||
.collect();
|
||||
if qself.is_some() {
|
||||
state
|
||||
.errors
|
||||
.error(TypePath { qself, path }, "unsupported struct path");
|
||||
return Err(());
|
||||
}
|
||||
let path = TypePath { qself, path };
|
||||
let resolved_path = state.resolve_enum_struct_path(path.clone())?;
|
||||
Self::struct_(
|
||||
state,
|
||||
MatchPatStruct {
|
||||
match_span: state.match_span,
|
||||
path,
|
||||
resolved_path,
|
||||
brace_token,
|
||||
fields,
|
||||
rest: rest.map(
|
||||
|
@ -400,6 +297,7 @@ trait ParseMatchPat: Sized {
|
|||
}| dot2_token,
|
||||
),
|
||||
},
|
||||
&path,
|
||||
)
|
||||
}
|
||||
Pat::TupleStruct(PatTupleStruct {
|
||||
|
@ -409,45 +307,45 @@ trait ParseMatchPat: Sized {
|
|||
paren_token,
|
||||
mut elems,
|
||||
}) => {
|
||||
let EnumPath {
|
||||
variant_path,
|
||||
enum_path,
|
||||
variant_name,
|
||||
} = parse_enum_path(TypePath { qself, path }).map_err(|path| {
|
||||
state.errors.error(path, "unsupported enum variant path");
|
||||
})?;
|
||||
if elems.is_empty() {
|
||||
let mut tokens = TokenStream::new();
|
||||
paren_token.surround(&mut tokens, |_| {});
|
||||
state.errors.error(
|
||||
tokens,
|
||||
"field-less enum variants must not be matched using parenthesis",
|
||||
);
|
||||
}
|
||||
if elems.len() != 1 {
|
||||
state.errors.error(
|
||||
variant_path,
|
||||
"enum variant pattern must have exactly one field",
|
||||
);
|
||||
return Err(());
|
||||
}
|
||||
let field = elems.pop().unwrap().into_value();
|
||||
let field = if let Pat::Rest(rest) = field {
|
||||
MatchPatSimple::Wild(MatchPatWild {
|
||||
underscore_token: Token![_](rest.dot2_token.span()),
|
||||
})
|
||||
let rest = if let Some(&Pat::Rest(PatRest {
|
||||
attrs: _,
|
||||
dot2_token,
|
||||
})) = elems.last()
|
||||
{
|
||||
elems.pop();
|
||||
Some(dot2_token)
|
||||
} else {
|
||||
MatchPatSimple::parse(state, field)?
|
||||
None
|
||||
};
|
||||
Self::enum_variant(
|
||||
let fields = elems
|
||||
.into_pairs()
|
||||
.enumerate()
|
||||
.filter_map(|(index, pair)| {
|
||||
let (pat, punct) = pair.into_tuple();
|
||||
let pat = MatchPatSimple::parse(state, pat).ok()?;
|
||||
let mut index = Index::from(index);
|
||||
index.span = state.span;
|
||||
let field = MatchPatStructField {
|
||||
member: index.into(),
|
||||
colon_token: Some(Token![:](state.span)),
|
||||
pat,
|
||||
};
|
||||
Some(Pair::new(field, punct))
|
||||
})
|
||||
.collect();
|
||||
let path = TypePath { qself, path };
|
||||
let resolved_path = state.resolve_enum_struct_path(path.clone())?;
|
||||
Self::struct_(
|
||||
state,
|
||||
MatchPatEnumVariant {
|
||||
match_span: state.match_span,
|
||||
variant_path,
|
||||
enum_path,
|
||||
variant_name,
|
||||
field: Some((paren_token, field)),
|
||||
MatchPatStruct {
|
||||
resolved_path,
|
||||
brace_token: Brace {
|
||||
span: paren_token.span,
|
||||
},
|
||||
fields,
|
||||
rest,
|
||||
},
|
||||
&path,
|
||||
)
|
||||
}
|
||||
Pat::Rest(_) => {
|
||||
|
@ -489,21 +387,14 @@ impl ParseMatchPat for MatchPatSimple {
|
|||
Self::Paren(v)
|
||||
}
|
||||
|
||||
fn struct_(state: &mut HdlMatchParseState<'_>, v: MatchPatStruct) -> Result<Self, ()> {
|
||||
state.errors.error(
|
||||
v.path,
|
||||
"matching structs is not yet implemented inside structs/enums in #[hdl] patterns",
|
||||
);
|
||||
Err(())
|
||||
}
|
||||
|
||||
fn enum_variant(
|
||||
fn struct_(
|
||||
state: &mut HdlMatchParseState<'_>,
|
||||
v: MatchPatEnumVariant,
|
||||
_v: MatchPatStruct,
|
||||
struct_error_spanned: &dyn ToTokens,
|
||||
) -> Result<Self, ()> {
|
||||
state.errors.error(
|
||||
v.variant_path,
|
||||
"matching enum variants is not yet implemented inside structs/enums in #[hdl] patterns",
|
||||
struct_error_spanned,
|
||||
"not yet implemented inside structs/enums in #[hdl] patterns",
|
||||
);
|
||||
Err(())
|
||||
}
|
||||
|
@ -515,7 +406,6 @@ enum MatchPat {
|
|||
Or(MatchPatOr<MatchPat>),
|
||||
Paren(MatchPatParen<MatchPat>),
|
||||
Struct(MatchPatStruct),
|
||||
EnumVariant(MatchPatEnumVariant),
|
||||
}
|
||||
|
||||
impl_fold! {
|
||||
|
@ -524,7 +414,6 @@ impl_fold! {
|
|||
Or(MatchPatOr<MatchPat>),
|
||||
Paren(MatchPatParen<MatchPat>),
|
||||
Struct(MatchPatStruct),
|
||||
EnumVariant(MatchPatEnumVariant),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -541,15 +430,12 @@ impl ParseMatchPat for MatchPat {
|
|||
Self::Paren(v)
|
||||
}
|
||||
|
||||
fn struct_(_state: &mut HdlMatchParseState<'_>, v: MatchPatStruct) -> Result<Self, ()> {
|
||||
Ok(Self::Struct(v))
|
||||
}
|
||||
|
||||
fn enum_variant(
|
||||
fn struct_(
|
||||
_state: &mut HdlMatchParseState<'_>,
|
||||
v: MatchPatEnumVariant,
|
||||
v: MatchPatStruct,
|
||||
_struct_error_spanned: &dyn ToTokens,
|
||||
) -> Result<Self, ()> {
|
||||
Ok(Self::EnumVariant(v))
|
||||
Ok(Self::Struct(v))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -560,7 +446,6 @@ impl ToTokens for MatchPat {
|
|||
Self::Or(v) => v.to_tokens(tokens),
|
||||
Self::Paren(v) => v.to_tokens(tokens),
|
||||
Self::Struct(v) => v.to_tokens(tokens),
|
||||
Self::EnumVariant(v) => v.to_tokens(tokens),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -626,96 +511,23 @@ impl Fold for RewriteAsCheckMatch {
|
|||
i.colon_token = Some(Token![:](i.member.span()));
|
||||
i
|
||||
}
|
||||
fn fold_pat(&mut self, pat: Pat) -> Pat {
|
||||
match pat {
|
||||
Pat::Ident(mut pat_ident) => match parse_enum_ident(pat_ident.ident) {
|
||||
Ok(EnumPath {
|
||||
variant_path: _,
|
||||
enum_path,
|
||||
variant_name,
|
||||
}) => parse_quote_spanned! {self.span=>
|
||||
__MatchTy::<#enum_path>::#variant_name {}
|
||||
},
|
||||
Err(ident) => {
|
||||
pat_ident.ident = ident;
|
||||
Pat::Ident(self.fold_pat_ident(pat_ident))
|
||||
}
|
||||
},
|
||||
Pat::Path(PatPath {
|
||||
attrs: _,
|
||||
qself,
|
||||
path,
|
||||
}) => match parse_enum_path(TypePath { qself, path }) {
|
||||
Ok(EnumPath {
|
||||
variant_path: _,
|
||||
enum_path,
|
||||
variant_name,
|
||||
}) => parse_quote_spanned! {self.span=>
|
||||
__MatchTy::<#enum_path>::#variant_name {}
|
||||
},
|
||||
Err(type_path) => parse_quote_spanned! {self.span=>
|
||||
__MatchTy::<#type_path> {}
|
||||
},
|
||||
},
|
||||
Pat::Struct(PatStruct {
|
||||
attrs: _,
|
||||
qself,
|
||||
path,
|
||||
brace_token,
|
||||
fields,
|
||||
rest,
|
||||
}) => {
|
||||
let type_path = TypePath { qself, path };
|
||||
let path = parse_quote_spanned! {self.span=>
|
||||
__MatchTy::<#type_path>
|
||||
};
|
||||
let fields = fields.do_fold(self);
|
||||
Pat::Struct(PatStruct {
|
||||
attrs: vec![],
|
||||
qself: None,
|
||||
path,
|
||||
brace_token,
|
||||
fields,
|
||||
rest,
|
||||
})
|
||||
}
|
||||
Pat::TupleStruct(PatTupleStruct {
|
||||
fn fold_pat(&mut self, i: Pat) -> Pat {
|
||||
match i {
|
||||
Pat::Ident(PatIdent {
|
||||
attrs,
|
||||
qself,
|
||||
path,
|
||||
paren_token,
|
||||
elems,
|
||||
}) => match parse_enum_path(TypePath { qself, path }) {
|
||||
Ok(EnumPath {
|
||||
variant_path: _,
|
||||
enum_path,
|
||||
variant_name,
|
||||
}) => {
|
||||
let path = parse_quote_spanned! {self.span=>
|
||||
__MatchTy::<#enum_path>::#variant_name
|
||||
};
|
||||
let elems = Punctuated::from_iter(
|
||||
elems.into_pairs().map_pair_value(|p| fold_pat(self, p)),
|
||||
);
|
||||
Pat::TupleStruct(PatTupleStruct {
|
||||
attrs,
|
||||
qself: None,
|
||||
path,
|
||||
paren_token,
|
||||
elems,
|
||||
})
|
||||
by_ref,
|
||||
mutability,
|
||||
ident,
|
||||
subpat: None,
|
||||
}) if is_pat_ident_a_struct_or_enum_name(&ident) => {
|
||||
parse_quote_spanned! {ident.span()=>
|
||||
#(#attrs)*
|
||||
#by_ref
|
||||
#mutability
|
||||
#ident {}
|
||||
}
|
||||
Err(TypePath { qself, path }) => {
|
||||
Pat::TupleStruct(self.fold_pat_tuple_struct(PatTupleStruct {
|
||||
attrs,
|
||||
qself,
|
||||
path,
|
||||
paren_token,
|
||||
elems,
|
||||
}))
|
||||
}
|
||||
},
|
||||
_ => fold_pat(self, pat),
|
||||
}
|
||||
_ => fold_pat(self, i),
|
||||
}
|
||||
}
|
||||
fn fold_pat_ident(&mut self, mut i: PatIdent) -> PatIdent {
|
||||
|
@ -743,14 +555,30 @@ impl Fold for RewriteAsCheckMatch {
|
|||
}
|
||||
|
||||
struct HdlMatchParseState<'a> {
|
||||
match_span: Span,
|
||||
errors: &'a mut Errors,
|
||||
span: Span,
|
||||
}
|
||||
|
||||
impl Visitor<'_> {
|
||||
impl HdlMatchParseState<'_> {
|
||||
fn resolve_enum_struct_path(&mut self, path: TypePath) -> Result<Path, ()> {
|
||||
let StructOrEnumPath { ty, variant } =
|
||||
StructOrEnumPath::new(self.errors, path, &AggregateLiteralOptions::default())?;
|
||||
Ok(if let Some((_variant_path, variant_name)) = variant {
|
||||
parse_quote_spanned! {self.span=>
|
||||
__MatchTy::<#ty>::#variant_name
|
||||
}
|
||||
} else {
|
||||
parse_quote_spanned! {self.span=>
|
||||
__MatchTy::<#ty>
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Visitor {
|
||||
pub(crate) fn process_hdl_match(
|
||||
&mut self,
|
||||
_hdl_attr: HdlAttr<Nothing, kw::hdl>,
|
||||
_hdl_attr: HdlAttr<Nothing>,
|
||||
expr_match: ExprMatch,
|
||||
) -> Expr {
|
||||
let span = expr_match.match_token.span();
|
||||
|
@ -762,24 +590,25 @@ impl Visitor<'_> {
|
|||
brace_token: _,
|
||||
arms,
|
||||
} = expr_match;
|
||||
self.require_normal_module_or_fn(match_token);
|
||||
self.require_normal_module(match_token);
|
||||
let mut state = HdlMatchParseState {
|
||||
match_span: span,
|
||||
errors: &mut self.errors,
|
||||
span,
|
||||
};
|
||||
let arms = Vec::from_iter(
|
||||
arms.into_iter()
|
||||
.filter_map(|arm| MatchArm::parse(&mut state, arm).ok()),
|
||||
);
|
||||
let expr = quote_spanned! {span=>
|
||||
parse_quote_spanned! {span=>
|
||||
{
|
||||
type __MatchTy<T> = <T as ::fayalite::ty::Type>::MatchVariant;
|
||||
type __MatchTy<V> =
|
||||
<<V as ::fayalite::expr::ToExpr>::Type as ::fayalite::ty::Type>::MatchVariant;
|
||||
let __match_expr = ::fayalite::expr::ToExpr::to_expr(&(#expr));
|
||||
::fayalite::expr::check_match_expr(__match_expr, |__match_value, __infallible| {
|
||||
#[allow(unused_variables)]
|
||||
#check_match
|
||||
});
|
||||
for __match_variant in ::fayalite::module::match_(__match_expr) {
|
||||
for __match_variant in m.match_(__match_expr) {
|
||||
let (__match_variant, __scope) =
|
||||
::fayalite::ty::MatchVariantAndInactiveScope::match_activate_scope(
|
||||
__match_variant,
|
||||
|
@ -789,7 +618,6 @@ impl Visitor<'_> {
|
|||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
syn::parse2(expr).unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
761
crates/fayalite-proc-macros-impl/src/value_derive_common.rs
Normal file
761
crates/fayalite-proc-macros-impl/src/value_derive_common.rs
Normal file
|
@ -0,0 +1,761 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use crate::{fold::impl_fold, kw, Errors, HdlAttr};
|
||||
use proc_macro2::{Span, TokenStream};
|
||||
use quote::{format_ident, quote, quote_spanned, ToTokens};
|
||||
use std::collections::{BTreeMap, HashMap, HashSet};
|
||||
use syn::{
|
||||
fold::{fold_generics, Fold},
|
||||
parse::{Parse, ParseStream},
|
||||
parse_quote, parse_quote_spanned,
|
||||
punctuated::Punctuated,
|
||||
spanned::Spanned,
|
||||
token::{Brace, Paren, Where},
|
||||
Block, ConstParam, Expr, Field, Fields, FieldsNamed, FieldsUnnamed, GenericParam, Generics,
|
||||
Ident, Index, ItemImpl, Lifetime, LifetimeParam, Member, Path, Token, Type, TypeParam,
|
||||
TypePath, Visibility, WhereClause, WherePredicate,
|
||||
};
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct Bounds(pub(crate) Punctuated<WherePredicate, Token![,]>);
|
||||
|
||||
impl_fold! {
|
||||
struct Bounds<>(Punctuated<WherePredicate, Token![,]>);
|
||||
}
|
||||
|
||||
impl Parse for Bounds {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
Ok(Bounds(Punctuated::parse_terminated(input)?))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Option<WhereClause>> for Bounds {
|
||||
fn from(value: Option<WhereClause>) -> Self {
|
||||
Self(value.map_or_else(Punctuated::new, |v| v.predicates))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Bounds {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
self.0.to_tokens(tokens)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct ParsedField<O> {
|
||||
pub(crate) options: HdlAttr<O>,
|
||||
pub(crate) vis: Visibility,
|
||||
pub(crate) name: Member,
|
||||
pub(crate) ty: Type,
|
||||
}
|
||||
|
||||
impl<O> ParsedField<O> {
|
||||
pub(crate) fn var_name(&self) -> Ident {
|
||||
format_ident!("__v_{}", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_field_name(
|
||||
index: usize,
|
||||
name: Option<Ident>,
|
||||
ty_span: impl FnOnce() -> Span,
|
||||
) -> Member {
|
||||
match name {
|
||||
Some(name) => Member::Named(name),
|
||||
None => Member::Unnamed(Index {
|
||||
index: index as _,
|
||||
span: ty_span(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_field_names(fields: &Fields) -> impl Iterator<Item = Member> + '_ {
|
||||
fields
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(index, field)| get_field_name(index, field.ident.clone(), || field.ty.span()))
|
||||
}
|
||||
|
||||
impl<O: Parse + Default> ParsedField<O> {
|
||||
pub(crate) fn parse_fields(
|
||||
errors: &mut Errors,
|
||||
fields: &mut Fields,
|
||||
in_enum: bool,
|
||||
) -> (FieldsKind, Vec<ParsedField<O>>) {
|
||||
let mut unit_fields = Punctuated::new();
|
||||
let (fields_kind, fields) = match fields {
|
||||
Fields::Named(fields) => (FieldsKind::Named(fields.brace_token), &mut fields.named),
|
||||
Fields::Unnamed(fields) => {
|
||||
(FieldsKind::Unnamed(fields.paren_token), &mut fields.unnamed)
|
||||
}
|
||||
Fields::Unit => (FieldsKind::Unit, &mut unit_fields),
|
||||
};
|
||||
let fields = fields
|
||||
.iter_mut()
|
||||
.enumerate()
|
||||
.map(|(index, field)| {
|
||||
let options = errors
|
||||
.unwrap_or_default(HdlAttr::parse_and_take_attr(&mut field.attrs))
|
||||
.unwrap_or_default();
|
||||
let name = get_field_name(index, field.ident.clone(), || field.ty.span());
|
||||
if in_enum && !matches!(field.vis, Visibility::Inherited) {
|
||||
errors.error(&field.vis, "field visibility not allowed in enums");
|
||||
}
|
||||
ParsedField {
|
||||
options,
|
||||
vis: field.vis.clone(),
|
||||
name,
|
||||
ty: field.ty.clone(),
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
(fields_kind, fields)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub(crate) enum FieldsKind {
|
||||
Unit,
|
||||
Named(Brace),
|
||||
Unnamed(Paren),
|
||||
}
|
||||
|
||||
impl FieldsKind {
|
||||
pub(crate) fn into_fields_named(
|
||||
brace_token: Brace,
|
||||
fields: impl IntoIterator<Item = syn::Field>,
|
||||
) -> Fields {
|
||||
Fields::Named(FieldsNamed {
|
||||
brace_token,
|
||||
named: Punctuated::from_iter(fields),
|
||||
})
|
||||
}
|
||||
pub(crate) fn into_fields_unnamed(
|
||||
paren_token: Paren,
|
||||
fields: impl IntoIterator<Item = syn::Field>,
|
||||
) -> Fields {
|
||||
Fields::Unnamed(FieldsUnnamed {
|
||||
paren_token,
|
||||
unnamed: Punctuated::from_iter(fields),
|
||||
})
|
||||
}
|
||||
pub(crate) fn into_fields(self, fields: impl IntoIterator<Item = syn::Field>) -> Fields {
|
||||
match self {
|
||||
FieldsKind::Unit => {
|
||||
let mut fields = fields.into_iter().peekable();
|
||||
let Some(first_field) = fields.peek() else {
|
||||
return Fields::Unit;
|
||||
};
|
||||
if first_field.ident.is_some() {
|
||||
Self::into_fields_named(Default::default(), fields)
|
||||
} else {
|
||||
Self::into_fields_unnamed(Default::default(), fields)
|
||||
}
|
||||
}
|
||||
FieldsKind::Named(brace_token) => Self::into_fields_named(brace_token, fields),
|
||||
FieldsKind::Unnamed(paren_token) => Self::into_fields_unnamed(paren_token, fields),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_target(target: &Option<(kw::target, Paren, Path)>, item_ident: &Ident) -> Path {
|
||||
match target {
|
||||
Some((_, _, target)) => target.clone(),
|
||||
None => item_ident.clone().into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ValueDeriveGenerics {
|
||||
pub(crate) generics: Generics,
|
||||
pub(crate) static_type_generics: Generics,
|
||||
}
|
||||
|
||||
impl ValueDeriveGenerics {
|
||||
pub(crate) fn get(mut generics: Generics, where_: &Option<(Where, Paren, Bounds)>) -> Self {
|
||||
let mut static_type_generics = generics.clone();
|
||||
if let Some((_, _, bounds)) = where_ {
|
||||
generics
|
||||
.make_where_clause()
|
||||
.predicates
|
||||
.extend(bounds.0.iter().cloned());
|
||||
static_type_generics
|
||||
.where_clause
|
||||
.clone_from(&generics.where_clause);
|
||||
} else {
|
||||
let type_params = Vec::from_iter(generics.type_params().map(|v| v.ident.clone()));
|
||||
let predicates = &mut generics.make_where_clause().predicates;
|
||||
let static_type_predicates = &mut static_type_generics.make_where_clause().predicates;
|
||||
for type_param in type_params {
|
||||
predicates.push(parse_quote! {#type_param: ::fayalite::ty::Value<Type: ::fayalite::ty::Type<Value = #type_param>>});
|
||||
static_type_predicates
|
||||
.push(parse_quote! {#type_param: ::fayalite::ty::StaticValue});
|
||||
}
|
||||
}
|
||||
Self {
|
||||
generics,
|
||||
static_type_generics,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn derive_clone_hash_eq_partialeq_for_struct<Name: ToTokens>(
|
||||
the_struct_ident: &Ident,
|
||||
generics: &Generics,
|
||||
field_names: &[Name],
|
||||
) -> TokenStream {
|
||||
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
|
||||
quote! {
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::__std::clone::Clone for #the_struct_ident #type_generics
|
||||
#where_clause
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
#(#field_names: ::fayalite::__std::clone::Clone::clone(&self.#field_names),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::__std::hash::Hash for #the_struct_ident #type_generics
|
||||
#where_clause
|
||||
{
|
||||
#[allow(unused_variables)]
|
||||
fn hash<__H: ::fayalite::__std::hash::Hasher>(&self, hasher: &mut __H) {
|
||||
#(::fayalite::__std::hash::Hash::hash(&self.#field_names, hasher);)*
|
||||
}
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::__std::cmp::Eq for #the_struct_ident #type_generics
|
||||
#where_clause
|
||||
{
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::__std::cmp::PartialEq for #the_struct_ident #type_generics
|
||||
#where_clause
|
||||
{
|
||||
#[allow(unused_variables)]
|
||||
#[allow(clippy::nonminimal_bool)]
|
||||
fn eq(&self, other: &Self) -> ::fayalite::__std::primitive::bool {
|
||||
true #(&& ::fayalite::__std::cmp::PartialEq::eq(
|
||||
&self.#field_names,
|
||||
&other.#field_names,
|
||||
))*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn append_field(fields: &mut Fields, mut field: Field) -> Member {
|
||||
let ident = field.ident.clone().expect("ident is supplied");
|
||||
match fields {
|
||||
Fields::Named(FieldsNamed { named, .. }) => {
|
||||
named.push(field);
|
||||
Member::Named(ident)
|
||||
}
|
||||
Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => {
|
||||
field.ident = None;
|
||||
field.colon_token = None;
|
||||
let index = unnamed.len();
|
||||
unnamed.push(field);
|
||||
Member::Unnamed(index.into())
|
||||
}
|
||||
Fields::Unit => {
|
||||
*fields = Fields::Named(FieldsNamed {
|
||||
brace_token: Default::default(),
|
||||
named: Punctuated::from_iter([field]),
|
||||
});
|
||||
Member::Named(ident)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct BuilderField {
|
||||
pub(crate) names: HashSet<Member>,
|
||||
pub(crate) mapped_value: Expr,
|
||||
pub(crate) mapped_type: Type,
|
||||
pub(crate) where_clause: Option<WhereClause>,
|
||||
pub(crate) builder_field_name: Ident,
|
||||
pub(crate) type_param: Ident,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Builder {
|
||||
struct_name: Ident,
|
||||
vis: Visibility,
|
||||
fields: BTreeMap<String, BuilderField>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct BuilderWithFields {
|
||||
struct_name: Ident,
|
||||
vis: Visibility,
|
||||
phantom_type_param: Ident,
|
||||
phantom_type_field: Ident,
|
||||
fields: Vec<(String, BuilderField)>,
|
||||
}
|
||||
|
||||
impl Builder {
|
||||
pub(crate) fn new(struct_name: Ident, vis: Visibility) -> Self {
|
||||
Self {
|
||||
struct_name,
|
||||
vis,
|
||||
fields: BTreeMap::new(),
|
||||
}
|
||||
}
|
||||
pub(crate) fn insert_field(
|
||||
&mut self,
|
||||
name: Member,
|
||||
map_value: impl FnOnce(&Ident) -> Expr,
|
||||
map_type: impl FnOnce(&Ident) -> Type,
|
||||
where_clause: impl FnOnce(&Ident) -> Option<WhereClause>,
|
||||
) {
|
||||
self.fields
|
||||
.entry(name.to_token_stream().to_string())
|
||||
.or_insert_with_key(|name| {
|
||||
let builder_field_name =
|
||||
format_ident!("field_{}", name, span = self.struct_name.span());
|
||||
let type_param = format_ident!("__T_{}", name, span = self.struct_name.span());
|
||||
BuilderField {
|
||||
names: HashSet::new(),
|
||||
mapped_value: map_value(&builder_field_name),
|
||||
mapped_type: map_type(&type_param),
|
||||
where_clause: where_clause(&type_param),
|
||||
builder_field_name,
|
||||
type_param,
|
||||
}
|
||||
})
|
||||
.names
|
||||
.insert(name);
|
||||
}
|
||||
pub(crate) fn finish_filling_in_fields(self) -> BuilderWithFields {
|
||||
let Self {
|
||||
struct_name,
|
||||
vis,
|
||||
fields,
|
||||
} = self;
|
||||
let fields = Vec::from_iter(fields);
|
||||
BuilderWithFields {
|
||||
phantom_type_param: Ident::new("__Phantom", struct_name.span()),
|
||||
phantom_type_field: Ident::new("__phantom", struct_name.span()),
|
||||
struct_name,
|
||||
vis,
|
||||
fields,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BuilderWithFields {
|
||||
pub(crate) fn get_field(&self, name: &Member) -> Option<(usize, &BuilderField)> {
|
||||
let index = self
|
||||
.fields
|
||||
.binary_search_by_key(&&*name.to_token_stream().to_string(), |v| &*v.0)
|
||||
.ok()?;
|
||||
Some((index, &self.fields[index].1))
|
||||
}
|
||||
pub(crate) fn ty(
|
||||
&self,
|
||||
specified_fields: impl IntoIterator<Item = (Member, Type)>,
|
||||
phantom_type: Option<&Type>,
|
||||
other_fields_are_any_type: bool,
|
||||
) -> TypePath {
|
||||
let Self {
|
||||
struct_name,
|
||||
vis: _,
|
||||
phantom_type_param,
|
||||
phantom_type_field: _,
|
||||
fields,
|
||||
} = self;
|
||||
let span = struct_name.span();
|
||||
let mut arguments =
|
||||
Vec::from_iter(fields.iter().map(|(_, BuilderField { type_param, .. })| {
|
||||
if other_fields_are_any_type {
|
||||
parse_quote_spanned! {span=>
|
||||
#type_param
|
||||
}
|
||||
} else {
|
||||
parse_quote_spanned! {span=>
|
||||
()
|
||||
}
|
||||
}
|
||||
}));
|
||||
for (name, ty) in specified_fields {
|
||||
let Some((index, _)) = self.get_field(&name) else {
|
||||
panic!("field not found: {}", name.to_token_stream());
|
||||
};
|
||||
arguments[index] = ty;
|
||||
}
|
||||
let phantom_type_param = phantom_type.is_none().then_some(phantom_type_param);
|
||||
parse_quote_spanned! {span=>
|
||||
#struct_name::<#phantom_type_param #phantom_type #(, #arguments)*>
|
||||
}
|
||||
}
|
||||
pub(crate) fn append_generics(
|
||||
&self,
|
||||
specified_fields: impl IntoIterator<Item = Member>,
|
||||
has_phantom_type_param: bool,
|
||||
other_fields_are_any_type: bool,
|
||||
generics: &mut Generics,
|
||||
) {
|
||||
let Self {
|
||||
struct_name: _,
|
||||
vis: _,
|
||||
phantom_type_param,
|
||||
phantom_type_field: _,
|
||||
fields,
|
||||
} = self;
|
||||
if has_phantom_type_param {
|
||||
generics.params.push(GenericParam::from(TypeParam::from(
|
||||
phantom_type_param.clone(),
|
||||
)));
|
||||
}
|
||||
if !other_fields_are_any_type {
|
||||
return;
|
||||
}
|
||||
let mut type_params = Vec::from_iter(
|
||||
fields
|
||||
.iter()
|
||||
.map(|(_, BuilderField { type_param, .. })| Some(type_param)),
|
||||
);
|
||||
for name in specified_fields {
|
||||
let Some((index, _)) = self.get_field(&name) else {
|
||||
panic!("field not found: {}", name.to_token_stream());
|
||||
};
|
||||
type_params[index] = None;
|
||||
}
|
||||
generics.params.extend(
|
||||
type_params
|
||||
.into_iter()
|
||||
.filter_map(|v| Some(GenericParam::from(TypeParam::from(v?.clone())))),
|
||||
);
|
||||
}
|
||||
pub(crate) fn make_build_method(
|
||||
&self,
|
||||
build_fn_name: &Ident,
|
||||
specified_fields: impl IntoIterator<Item = (Member, Type)>,
|
||||
generics: &Generics,
|
||||
phantom_type: &Type,
|
||||
return_ty: &Type,
|
||||
mut body: Block,
|
||||
) -> ItemImpl {
|
||||
let Self {
|
||||
struct_name,
|
||||
vis,
|
||||
phantom_type_param: _,
|
||||
phantom_type_field,
|
||||
fields,
|
||||
} = self;
|
||||
let span = struct_name.span();
|
||||
let field_names = Vec::from_iter(fields.iter().map(|v| &v.1.builder_field_name));
|
||||
let (impl_generics, _type_generics, where_clause) = generics.split_for_impl();
|
||||
let empty_arg = parse_quote_spanned! {span=>
|
||||
()
|
||||
};
|
||||
let mut ty_arguments = vec![empty_arg; fields.len()];
|
||||
let empty_field_pat = quote_spanned! {span=>
|
||||
: _
|
||||
};
|
||||
let mut field_pats = vec![Some(empty_field_pat); fields.len()];
|
||||
for (name, ty) in specified_fields {
|
||||
let Some((index, _)) = self.get_field(&name) else {
|
||||
panic!("field not found: {}", name.to_token_stream());
|
||||
};
|
||||
ty_arguments[index] = ty;
|
||||
field_pats[index] = None;
|
||||
}
|
||||
body.stmts.insert(
|
||||
0,
|
||||
parse_quote_spanned! {span=>
|
||||
let Self {
|
||||
#(#field_names #field_pats,)*
|
||||
#phantom_type_field: _,
|
||||
} = self;
|
||||
},
|
||||
);
|
||||
parse_quote_spanned! {span=>
|
||||
#[automatically_derived]
|
||||
impl #impl_generics #struct_name<#phantom_type #(, #ty_arguments)*>
|
||||
#where_clause
|
||||
{
|
||||
#[allow(non_snake_case, dead_code)]
|
||||
#vis fn #build_fn_name(self) -> #return_ty
|
||||
#body
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for BuilderWithFields {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self {
|
||||
struct_name,
|
||||
vis,
|
||||
phantom_type_param,
|
||||
phantom_type_field,
|
||||
fields,
|
||||
} = self;
|
||||
let span = struct_name.span();
|
||||
let mut any_generics = Generics::default();
|
||||
self.append_generics([], true, true, &mut any_generics);
|
||||
let empty_ty = self.ty([], None, false);
|
||||
let field_names = Vec::from_iter(fields.iter().map(|v| &v.1.builder_field_name));
|
||||
let field_type_params = Vec::from_iter(fields.iter().map(|v| &v.1.type_param));
|
||||
quote_spanned! {span=>
|
||||
#[allow(non_camel_case_types)]
|
||||
#[non_exhaustive]
|
||||
#vis struct #struct_name #any_generics {
|
||||
#(#field_names: #field_type_params,)*
|
||||
#phantom_type_field: ::fayalite::__std::marker::PhantomData<#phantom_type_param>,
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl<#phantom_type_param> #empty_ty {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
#(#field_names: (),)*
|
||||
#phantom_type_field: ::fayalite::__std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
for (field_index, (_, field)) in self.fields.iter().enumerate() {
|
||||
let initial_fields = &fields[..field_index];
|
||||
let final_fields = &fields[field_index..][1..];
|
||||
let initial_type_params =
|
||||
Vec::from_iter(initial_fields.iter().map(|v| &v.1.type_param));
|
||||
let final_type_params = Vec::from_iter(final_fields.iter().map(|v| &v.1.type_param));
|
||||
let initial_field_names =
|
||||
Vec::from_iter(initial_fields.iter().map(|v| &v.1.builder_field_name));
|
||||
let final_field_names =
|
||||
Vec::from_iter(final_fields.iter().map(|v| &v.1.builder_field_name));
|
||||
let BuilderField {
|
||||
names: _,
|
||||
mapped_value,
|
||||
mapped_type,
|
||||
where_clause,
|
||||
builder_field_name,
|
||||
type_param,
|
||||
} = field;
|
||||
quote_spanned! {span=>
|
||||
#[automatically_derived]
|
||||
#[allow(non_camel_case_types, dead_code)]
|
||||
impl<#phantom_type_param #(, #initial_type_params)* #(, #final_type_params)*>
|
||||
#struct_name<
|
||||
#phantom_type_param,
|
||||
#(#initial_type_params,)*
|
||||
(), #(#final_type_params,)*
|
||||
>
|
||||
{
|
||||
#vis fn #builder_field_name<#type_param>(
|
||||
self,
|
||||
#builder_field_name: #type_param,
|
||||
) -> #struct_name<
|
||||
#phantom_type_param,
|
||||
#(#initial_type_params,)*
|
||||
#mapped_type,
|
||||
#(#final_type_params,)*
|
||||
>
|
||||
#where_clause
|
||||
{
|
||||
let Self {
|
||||
#(#initial_field_names,)*
|
||||
#builder_field_name: (),
|
||||
#(#final_field_names,)*
|
||||
#phantom_type_field: _,
|
||||
} = self;
|
||||
let #builder_field_name = #mapped_value;
|
||||
#struct_name {
|
||||
#(#field_names,)*
|
||||
#phantom_type_field: ::fayalite::__std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct MapIdents {
|
||||
pub(crate) map: HashMap<Ident, Ident>,
|
||||
}
|
||||
|
||||
impl Fold for &MapIdents {
|
||||
fn fold_ident(&mut self, i: Ident) -> Ident {
|
||||
self.map.get(&i).cloned().unwrap_or(i)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct DupGenerics<M> {
|
||||
pub(crate) combined: Generics,
|
||||
pub(crate) maps: M,
|
||||
}
|
||||
|
||||
pub(crate) fn merge_punctuated<T, P: Default>(
|
||||
target: &mut Punctuated<T, P>,
|
||||
source: Punctuated<T, P>,
|
||||
make_punct: impl FnOnce() -> P,
|
||||
) {
|
||||
if source.is_empty() {
|
||||
return;
|
||||
}
|
||||
if target.is_empty() {
|
||||
*target = source;
|
||||
return;
|
||||
}
|
||||
if !target.trailing_punct() {
|
||||
target.push_punct(make_punct());
|
||||
}
|
||||
target.extend(source.into_pairs());
|
||||
}
|
||||
|
||||
pub(crate) fn merge_generics(target: &mut Generics, source: Generics) {
|
||||
let Generics {
|
||||
lt_token,
|
||||
params,
|
||||
gt_token,
|
||||
where_clause,
|
||||
} = source;
|
||||
let span = lt_token.map(|v| v.span).unwrap_or_else(|| params.span());
|
||||
target.lt_token = target.lt_token.or(lt_token);
|
||||
merge_punctuated(&mut target.params, params, || Token![,](span));
|
||||
target.gt_token = target.gt_token.or(gt_token);
|
||||
if let Some(where_clause) = where_clause {
|
||||
if let Some(target_where_clause) = &mut target.where_clause {
|
||||
let WhereClause {
|
||||
where_token,
|
||||
predicates,
|
||||
} = where_clause;
|
||||
let span = where_token.span;
|
||||
target_where_clause.where_token = where_token;
|
||||
merge_punctuated(&mut target_where_clause.predicates, predicates, || {
|
||||
Token![,](span)
|
||||
});
|
||||
} else {
|
||||
target.where_clause = Some(where_clause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DupGenerics<Vec<MapIdents>> {
|
||||
pub(crate) fn new_dyn(generics: &Generics, count: usize) -> Self {
|
||||
let mut maps = Vec::from_iter((0..count).map(|_| MapIdents {
|
||||
map: HashMap::new(),
|
||||
}));
|
||||
for param in &generics.params {
|
||||
let (GenericParam::Lifetime(LifetimeParam {
|
||||
lifetime: Lifetime { ident, .. },
|
||||
..
|
||||
})
|
||||
| GenericParam::Type(TypeParam { ident, .. })
|
||||
| GenericParam::Const(ConstParam { ident, .. })) = param;
|
||||
for (i, map_idents) in maps.iter_mut().enumerate() {
|
||||
map_idents
|
||||
.map
|
||||
.insert(ident.clone(), format_ident!("__{}_{}", ident, i));
|
||||
}
|
||||
}
|
||||
let mut combined = Generics::default();
|
||||
for map_idents in maps.iter() {
|
||||
merge_generics(
|
||||
&mut combined,
|
||||
fold_generics(&mut { map_idents }, generics.clone()),
|
||||
);
|
||||
}
|
||||
Self { combined, maps }
|
||||
}
|
||||
}
|
||||
|
||||
impl<const COUNT: usize> DupGenerics<[MapIdents; COUNT]> {
|
||||
pub(crate) fn new(generics: &Generics) -> Self {
|
||||
let DupGenerics { combined, maps } = DupGenerics::new_dyn(generics, COUNT);
|
||||
Self {
|
||||
combined,
|
||||
maps: maps.try_into().ok().unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn add_where_predicate(
|
||||
target: &mut Generics,
|
||||
span: Span,
|
||||
where_predicate: WherePredicate,
|
||||
) {
|
||||
let WhereClause {
|
||||
where_token: _,
|
||||
predicates,
|
||||
} = target.where_clause.get_or_insert_with(|| WhereClause {
|
||||
where_token: Token![where](span),
|
||||
predicates: Punctuated::new(),
|
||||
});
|
||||
if !predicates.empty_or_trailing() {
|
||||
predicates.push_punct(Token![,](span));
|
||||
}
|
||||
predicates.push_value(where_predicate);
|
||||
}
|
||||
|
||||
pub(crate) fn make_connect_impl(
|
||||
connect_inexact: Option<(crate::kw::connect_inexact,)>,
|
||||
generics: &Generics,
|
||||
ty_ident: &Ident,
|
||||
field_types: impl IntoIterator<Item = Type>,
|
||||
) -> TokenStream {
|
||||
let span = ty_ident.span();
|
||||
let impl_generics;
|
||||
let combined_generics;
|
||||
let where_clause;
|
||||
let lhs_generics;
|
||||
let lhs_type_generics;
|
||||
let rhs_generics;
|
||||
let rhs_type_generics;
|
||||
if connect_inexact.is_some() {
|
||||
let DupGenerics {
|
||||
mut combined,
|
||||
maps: [lhs_map, rhs_map],
|
||||
} = DupGenerics::new(generics);
|
||||
for field_type in field_types {
|
||||
let lhs_type = (&lhs_map).fold_type(field_type.clone());
|
||||
let rhs_type = (&rhs_map).fold_type(field_type);
|
||||
add_where_predicate(
|
||||
&mut combined,
|
||||
span,
|
||||
parse_quote_spanned! {span=>
|
||||
#lhs_type: ::fayalite::ty::Connect<#rhs_type>
|
||||
},
|
||||
);
|
||||
}
|
||||
combined_generics = combined;
|
||||
(impl_generics, _, where_clause) = combined_generics.split_for_impl();
|
||||
lhs_generics = (&lhs_map).fold_generics(generics.clone());
|
||||
(_, lhs_type_generics, _) = lhs_generics.split_for_impl();
|
||||
rhs_generics = (&rhs_map).fold_generics(generics.clone());
|
||||
(_, rhs_type_generics, _) = rhs_generics.split_for_impl();
|
||||
} else {
|
||||
let mut generics = generics.clone();
|
||||
for field_type in field_types {
|
||||
add_where_predicate(
|
||||
&mut generics,
|
||||
span,
|
||||
parse_quote_spanned! {span=>
|
||||
#field_type: ::fayalite::ty::Connect<#field_type>
|
||||
},
|
||||
);
|
||||
}
|
||||
combined_generics = generics;
|
||||
(impl_generics, lhs_type_generics, where_clause) = combined_generics.split_for_impl();
|
||||
rhs_type_generics = lhs_type_generics.clone();
|
||||
}
|
||||
quote_spanned! {span=>
|
||||
#[automatically_derived]
|
||||
#[allow(non_camel_case_types)]
|
||||
impl #impl_generics ::fayalite::ty::Connect<#ty_ident #rhs_type_generics>
|
||||
for #ty_ident #lhs_type_generics
|
||||
#where_clause
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
969
crates/fayalite-proc-macros-impl/src/value_derive_enum.rs
Normal file
969
crates/fayalite-proc-macros-impl/src/value_derive_enum.rs
Normal file
|
@ -0,0 +1,969 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
value_derive_common::{
|
||||
append_field, derive_clone_hash_eq_partialeq_for_struct, get_field_names, get_target,
|
||||
make_connect_impl, Bounds, Builder, FieldsKind, ParsedField, ValueDeriveGenerics,
|
||||
},
|
||||
value_derive_struct::{self, ParsedStruct, ParsedStructNames, StructOptions},
|
||||
Errors, HdlAttr,
|
||||
};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote, quote_spanned, ToTokens};
|
||||
use syn::{
|
||||
parse_quote, parse_quote_spanned, punctuated::Punctuated, spanned::Spanned, token::Brace,
|
||||
Field, FieldMutability, Fields, FieldsNamed, Generics, Ident, Index, ItemEnum, ItemStruct,
|
||||
Member, Path, Token, Type, Variant, Visibility,
|
||||
};
|
||||
|
||||
crate::options! {
|
||||
#[options = EnumOptions]
|
||||
enum EnumOption {
|
||||
OutlineGenerated(outline_generated),
|
||||
ConnectInexact(connect_inexact),
|
||||
Bounds(where_, Bounds),
|
||||
Target(target, Path),
|
||||
}
|
||||
}
|
||||
|
||||
crate::options! {
|
||||
#[options = VariantOptions]
|
||||
enum VariantOption {}
|
||||
}
|
||||
|
||||
crate::options! {
|
||||
#[options = FieldOptions]
|
||||
enum FieldOption {}
|
||||
}
|
||||
|
||||
enum VariantValue {
|
||||
None,
|
||||
Direct {
|
||||
value_type: Type,
|
||||
},
|
||||
Struct {
|
||||
value_struct: ItemStruct,
|
||||
parsed_struct: ParsedStruct,
|
||||
},
|
||||
}
|
||||
|
||||
impl VariantValue {
|
||||
fn is_none(&self) -> bool {
|
||||
matches!(self, Self::None)
|
||||
}
|
||||
fn value_ty(&self) -> Option<Type> {
|
||||
match self {
|
||||
VariantValue::None => None,
|
||||
VariantValue::Direct { value_type } => Some(value_type.clone()),
|
||||
VariantValue::Struct { value_struct, .. } => {
|
||||
let (_, type_generics, _) = value_struct.generics.split_for_impl();
|
||||
let ident = &value_struct.ident;
|
||||
Some(parse_quote! { #ident #type_generics })
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ParsedVariant {
|
||||
options: HdlAttr<VariantOptions>,
|
||||
ident: Ident,
|
||||
fields_kind: FieldsKind,
|
||||
fields: Vec<ParsedField<FieldOptions>>,
|
||||
value: VariantValue,
|
||||
}
|
||||
|
||||
impl ParsedVariant {
|
||||
fn parse(
|
||||
errors: &mut Errors,
|
||||
variant: Variant,
|
||||
enum_options: &EnumOptions,
|
||||
enum_vis: &Visibility,
|
||||
enum_ident: &Ident,
|
||||
enum_generics: &Generics,
|
||||
) -> Self {
|
||||
let target = get_target(&enum_options.target, enum_ident);
|
||||
let Variant {
|
||||
mut attrs,
|
||||
ident,
|
||||
fields,
|
||||
discriminant,
|
||||
} = variant;
|
||||
if let Some((eq, _)) = discriminant {
|
||||
errors.error(eq, "#[derive(Value)]: discriminants not allowed");
|
||||
}
|
||||
let variant_options = errors
|
||||
.unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs))
|
||||
.unwrap_or_default();
|
||||
let (fields_kind, parsed_fields) =
|
||||
ParsedField::parse_fields(errors, &mut fields.clone(), true);
|
||||
let value = match (&fields_kind, &*parsed_fields) {
|
||||
(FieldsKind::Unit, _) => VariantValue::None,
|
||||
(
|
||||
FieldsKind::Unnamed(_),
|
||||
[ParsedField {
|
||||
options,
|
||||
vis: _,
|
||||
name: Member::Unnamed(Index { index: 0, span: _ }),
|
||||
ty,
|
||||
}],
|
||||
) => {
|
||||
let FieldOptions {} = options.body;
|
||||
VariantValue::Direct {
|
||||
value_type: ty.clone(),
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let variant_value_struct_ident =
|
||||
format_ident!("__{}__{}", enum_ident, ident, span = ident.span());
|
||||
let variant_type_struct_ident =
|
||||
format_ident!("__{}__{}__Type", enum_ident, ident, span = ident.span());
|
||||
let mut value_struct_fields = fields.clone();
|
||||
let (_, type_generics, _) = enum_generics.split_for_impl();
|
||||
append_field(
|
||||
&mut value_struct_fields,
|
||||
Field {
|
||||
attrs: vec![HdlAttr::from(value_derive_struct::FieldOptions {
|
||||
flip: None,
|
||||
skip: Some(Default::default()),
|
||||
})
|
||||
.to_attr()],
|
||||
vis: enum_vis.clone(),
|
||||
mutability: FieldMutability::None,
|
||||
ident: Some(Ident::new("__phantom", ident.span())),
|
||||
colon_token: None,
|
||||
ty: parse_quote_spanned! {ident.span()=>
|
||||
::fayalite::__std::marker::PhantomData<#target #type_generics>
|
||||
},
|
||||
},
|
||||
);
|
||||
let (value_struct_fields_kind, value_struct_parsed_fields) =
|
||||
ParsedField::parse_fields(errors, &mut value_struct_fields, false);
|
||||
let value_struct = ItemStruct {
|
||||
attrs: vec![parse_quote! { #[allow(non_camel_case_types)] }],
|
||||
vis: enum_vis.clone(),
|
||||
struct_token: Token![struct](ident.span()),
|
||||
ident: variant_value_struct_ident.clone(),
|
||||
generics: enum_generics.clone(),
|
||||
fields: value_struct_fields,
|
||||
semi_token: None,
|
||||
};
|
||||
VariantValue::Struct {
|
||||
value_struct,
|
||||
parsed_struct: ParsedStruct {
|
||||
options: StructOptions {
|
||||
outline_generated: None,
|
||||
static_: Some(Default::default()),
|
||||
where_: Some((
|
||||
Default::default(),
|
||||
Default::default(),
|
||||
ValueDeriveGenerics::get(
|
||||
enum_generics.clone(),
|
||||
&enum_options.where_,
|
||||
)
|
||||
.static_type_generics
|
||||
.where_clause
|
||||
.into(),
|
||||
)),
|
||||
target: None,
|
||||
connect_inexact: enum_options.connect_inexact,
|
||||
}
|
||||
.into(),
|
||||
vis: enum_vis.clone(),
|
||||
struct_token: Default::default(),
|
||||
generics: enum_generics.clone(),
|
||||
fields_kind: value_struct_fields_kind,
|
||||
fields: value_struct_parsed_fields,
|
||||
semi_token: None, // it will fill in the semicolon if needed
|
||||
skip_check_fields: true,
|
||||
names: ParsedStructNames {
|
||||
ident: variant_value_struct_ident.clone(),
|
||||
type_struct_debug_ident: Some(format!("{enum_ident}::{ident}::Type")),
|
||||
type_struct_ident: variant_type_struct_ident,
|
||||
match_variant_ident: None,
|
||||
builder_struct_ident: None,
|
||||
mask_match_variant_ident: None,
|
||||
mask_type_ident: None,
|
||||
mask_type_debug_ident: Some(format!(
|
||||
"AsMask<{enum_ident}::{ident}>::Type"
|
||||
)),
|
||||
mask_value_ident: None,
|
||||
mask_value_debug_ident: Some(format!("AsMask<{enum_ident}::{ident}>")),
|
||||
mask_builder_struct_ident: None,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
};
|
||||
ParsedVariant {
|
||||
options: variant_options,
|
||||
ident,
|
||||
fields_kind,
|
||||
fields: parsed_fields,
|
||||
value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ParsedEnum {
|
||||
options: HdlAttr<EnumOptions>,
|
||||
vis: Visibility,
|
||||
enum_token: Token![enum],
|
||||
ident: Ident,
|
||||
generics: Generics,
|
||||
brace_token: Brace,
|
||||
variants: Vec<ParsedVariant>,
|
||||
}
|
||||
|
||||
impl ParsedEnum {
|
||||
fn parse(item: ItemEnum) -> syn::Result<Self> {
|
||||
let ItemEnum {
|
||||
mut attrs,
|
||||
vis,
|
||||
enum_token,
|
||||
ident,
|
||||
generics,
|
||||
brace_token,
|
||||
variants,
|
||||
} = item;
|
||||
let mut errors = Errors::new();
|
||||
let enum_options = errors
|
||||
.unwrap_or_default(HdlAttr::parse_and_take_attr(&mut attrs))
|
||||
.unwrap_or_default();
|
||||
let variants = variants
|
||||
.into_iter()
|
||||
.map(|variant| {
|
||||
ParsedVariant::parse(
|
||||
&mut errors,
|
||||
variant,
|
||||
&enum_options.body,
|
||||
&vis,
|
||||
&ident,
|
||||
&generics,
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
errors.finish()?;
|
||||
Ok(ParsedEnum {
|
||||
options: enum_options,
|
||||
vis,
|
||||
enum_token,
|
||||
ident,
|
||||
generics,
|
||||
brace_token,
|
||||
variants,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for ParsedEnum {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let Self {
|
||||
options,
|
||||
vis,
|
||||
enum_token,
|
||||
ident: enum_ident,
|
||||
generics: enum_generics,
|
||||
brace_token,
|
||||
variants,
|
||||
} = self;
|
||||
let EnumOptions {
|
||||
outline_generated: _,
|
||||
connect_inexact,
|
||||
where_,
|
||||
target,
|
||||
} = &options.body;
|
||||
let target = get_target(target, enum_ident);
|
||||
let ValueDeriveGenerics {
|
||||
generics: _,
|
||||
static_type_generics,
|
||||
} = ValueDeriveGenerics::get(enum_generics.clone(), where_);
|
||||
let (static_type_impl_generics, static_type_type_generics, static_type_where_clause) =
|
||||
static_type_generics.split_for_impl();
|
||||
let type_struct_ident = format_ident!("__{}__Type", enum_ident);
|
||||
let mut field_checks = vec![];
|
||||
let mut make_type_struct_variant_type = |variant: &ParsedVariant| {
|
||||
let VariantOptions {} = variant.options.body;
|
||||
let (value_struct, parsed_struct) = match &variant.value {
|
||||
VariantValue::None => {
|
||||
return None;
|
||||
}
|
||||
VariantValue::Direct { value_type } => {
|
||||
field_checks.push(quote_spanned! {value_type.span()=>
|
||||
__check_field::<#value_type>();
|
||||
});
|
||||
return Some(parse_quote! { <#value_type as ::fayalite::expr::ToExpr>::Type });
|
||||
}
|
||||
VariantValue::Struct {
|
||||
value_struct,
|
||||
parsed_struct,
|
||||
} => (value_struct, parsed_struct),
|
||||
};
|
||||
value_struct.to_tokens(tokens);
|
||||
parsed_struct.to_tokens(tokens);
|
||||
let mut field_names = Vec::from_iter(get_field_names(&value_struct.fields));
|
||||
derive_clone_hash_eq_partialeq_for_struct(
|
||||
&value_struct.ident,
|
||||
&static_type_generics,
|
||||
&field_names,
|
||||
)
|
||||
.to_tokens(tokens);
|
||||
field_names = Vec::from_iter(
|
||||
field_names
|
||||
.into_iter()
|
||||
.zip(parsed_struct.fields.iter())
|
||||
.filter_map(|(member, field)| {
|
||||
field.options.body.skip.is_none().then_some(member)
|
||||
}),
|
||||
);
|
||||
let field_name_strs =
|
||||
Vec::from_iter(field_names.iter().map(|v| v.to_token_stream().to_string()));
|
||||
let debug_ident = format!("{enum_ident}::{}", variant.ident);
|
||||
let debug_body = match variant.fields_kind {
|
||||
FieldsKind::Unit => quote! {
|
||||
f.debug_struct(#debug_ident).finish()
|
||||
},
|
||||
FieldsKind::Named(_) => quote! {
|
||||
f.debug_struct(#debug_ident)
|
||||
#(.field(#field_name_strs, &self.#field_names))*
|
||||
.finish()
|
||||
},
|
||||
FieldsKind::Unnamed(_) => quote! {
|
||||
f.debug_tuple(#debug_ident)#(.field(&self.#field_names))*.finish()
|
||||
},
|
||||
};
|
||||
let value_struct_ident = &value_struct.ident;
|
||||
quote! {
|
||||
#[automatically_derived]
|
||||
impl #static_type_impl_generics ::fayalite::__std::fmt::Debug
|
||||
for #value_struct_ident #static_type_type_generics
|
||||
#static_type_where_clause
|
||||
{
|
||||
fn fmt(
|
||||
&self,
|
||||
f: &mut ::fayalite::__std::fmt::Formatter<'_>,
|
||||
) -> ::fayalite::__std::fmt::Result {
|
||||
#debug_body
|
||||
}
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
Some(parse_quote! {
|
||||
<
|
||||
#value_struct_ident #static_type_type_generics
|
||||
as ::fayalite::expr::ToExpr
|
||||
>::Type
|
||||
})
|
||||
};
|
||||
let type_struct_variants = Punctuated::from_iter(variants.iter().filter_map(|variant| {
|
||||
let VariantOptions {} = variant.options.body;
|
||||
Some(Field {
|
||||
attrs: vec![],
|
||||
vis: vis.clone(),
|
||||
mutability: FieldMutability::None,
|
||||
ident: Some(variant.ident.clone()),
|
||||
colon_token: None, // it will fill in the colon if needed
|
||||
ty: make_type_struct_variant_type(variant)?,
|
||||
})
|
||||
}));
|
||||
let type_struct = ItemStruct {
|
||||
attrs: vec![
|
||||
parse_quote! {#[allow(non_camel_case_types)]},
|
||||
parse_quote! {#[allow(non_snake_case)]},
|
||||
],
|
||||
vis: vis.clone(),
|
||||
struct_token: Token![struct](enum_token.span),
|
||||
ident: type_struct_ident,
|
||||
generics: static_type_generics.clone(),
|
||||
fields: Fields::Named(FieldsNamed {
|
||||
brace_token: *brace_token,
|
||||
named: type_struct_variants,
|
||||
}),
|
||||
semi_token: None,
|
||||
};
|
||||
let type_struct_ident = &type_struct.ident;
|
||||
let type_struct_debug_ident = format!("{enum_ident}::Type");
|
||||
type_struct.to_tokens(tokens);
|
||||
let non_empty_variant_names = Vec::from_iter(
|
||||
variants
|
||||
.iter()
|
||||
.filter(|v| !v.value.is_none())
|
||||
.map(|v| v.ident.clone()),
|
||||
);
|
||||
let non_empty_variant_name_strs =
|
||||
Vec::from_iter(non_empty_variant_names.iter().map(|v| v.to_string()));
|
||||
let debug_type_body = quote! {
|
||||
f.debug_struct(#type_struct_debug_ident)
|
||||
#(.field(#non_empty_variant_name_strs, &self.#non_empty_variant_names))*
|
||||
.finish()
|
||||
};
|
||||
derive_clone_hash_eq_partialeq_for_struct(
|
||||
type_struct_ident,
|
||||
&static_type_generics,
|
||||
&non_empty_variant_names,
|
||||
)
|
||||
.to_tokens(tokens);
|
||||
let variant_names = Vec::from_iter(variants.iter().map(|v| &v.ident));
|
||||
let variant_name_strs = Vec::from_iter(variant_names.iter().map(|v| v.to_string()));
|
||||
let (variant_field_pats, variant_to_canonical_values): (Vec<_>, Vec<_>) = variants
|
||||
.iter()
|
||||
.map(|v| {
|
||||
let field_names: Vec<_> = v.fields.iter().map(|field| &field.name).collect();
|
||||
let var_names: Vec<_> = v.fields.iter().map(|field| field.var_name()).collect();
|
||||
let field_pats = quote! {
|
||||
#(#field_names: #var_names,)*
|
||||
};
|
||||
let to_canonical_value = match &v.value {
|
||||
VariantValue::None => quote! { ::fayalite::__std::option::Option::None },
|
||||
VariantValue::Direct { .. } => {
|
||||
debug_assert_eq!(var_names.len(), 1);
|
||||
quote! {
|
||||
::fayalite::__std::option::Option::Some(
|
||||
::fayalite::ty::DynValueTrait::to_canonical_dyn(#(#var_names)*),
|
||||
)
|
||||
}
|
||||
}
|
||||
VariantValue::Struct {
|
||||
value_struct,
|
||||
parsed_struct,
|
||||
} => {
|
||||
let value_struct_ident = &value_struct.ident;
|
||||
let phantom_field_name = &parsed_struct
|
||||
.fields
|
||||
.last()
|
||||
.expect("missing phantom field")
|
||||
.name;
|
||||
let type_generics = static_type_type_generics.as_turbofish();
|
||||
quote! {
|
||||
::fayalite::__std::option::Option::Some(
|
||||
::fayalite::ty::DynValueTrait::to_canonical_dyn(
|
||||
&#value_struct_ident #type_generics {
|
||||
#(#field_names:
|
||||
::fayalite::__std::clone::Clone::clone(#var_names),)*
|
||||
#phantom_field_name: ::fayalite::__std::marker::PhantomData,
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
(field_pats, to_canonical_value)
|
||||
})
|
||||
.unzip();
|
||||
let mut match_enum_variants = Punctuated::new();
|
||||
let mut match_enum_debug_arms = vec![];
|
||||
let mut match_enum_arms = vec![];
|
||||
let mut variant_vars = vec![];
|
||||
let mut from_canonical_type_variant_lets = vec![];
|
||||
let mut non_empty_variant_vars = vec![];
|
||||
let mut enum_type_variants = vec![];
|
||||
let mut enum_type_variants_hint = vec![];
|
||||
let match_enum_ident = format_ident!("__{}__MatchEnum", enum_ident);
|
||||
let mut builder = Builder::new(format_ident!("__{}__Builder", enum_ident), vis.clone());
|
||||
for variant in variants.iter() {
|
||||
for field in variant.fields.iter() {
|
||||
builder.insert_field(
|
||||
field.name.clone(),
|
||||
|v| {
|
||||
parse_quote_spanned! {v.span()=>
|
||||
::fayalite::expr::ToExpr::to_expr(&#v)
|
||||
}
|
||||
},
|
||||
|t| {
|
||||
parse_quote_spanned! {t.span()=>
|
||||
::fayalite::expr::Expr<<
|
||||
<#t as ::fayalite::expr::ToExpr>::Type
|
||||
as ::fayalite::ty::Type
|
||||
>::Value>
|
||||
}
|
||||
},
|
||||
|t| {
|
||||
parse_quote_spanned! {t.span()=>
|
||||
where
|
||||
#t: ::fayalite::expr::ToExpr,
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
let builder = builder.finish_filling_in_fields();
|
||||
builder.to_tokens(tokens);
|
||||
for (variant_index, variant) in variants.iter().enumerate() {
|
||||
let variant_var = format_ident!("__v_{}", variant.ident);
|
||||
let variant_name = &variant.ident;
|
||||
let variant_name_str = variant.ident.to_string();
|
||||
match_enum_variants.push(Variant {
|
||||
attrs: vec![],
|
||||
ident: variant.ident.clone(),
|
||||
fields: variant.fields_kind.into_fields(variant.fields.iter().map(
|
||||
|ParsedField {
|
||||
options,
|
||||
vis,
|
||||
name,
|
||||
ty,
|
||||
}| {
|
||||
let FieldOptions {} = options.body;
|
||||
Field {
|
||||
attrs: vec![],
|
||||
vis: vis.clone(),
|
||||
mutability: FieldMutability::None,
|
||||
ident: if let Member::Named(name) = name {
|
||||
Some(name.clone())
|
||||
} else {
|
||||
None
|
||||
},
|
||||
colon_token: None,
|
||||
ty: parse_quote! { ::fayalite::expr::Expr<#ty> },
|
||||
}
|
||||
},
|
||||
)),
|
||||
discriminant: None,
|
||||
});
|
||||
let match_enum_field_names = Vec::from_iter(variant.fields.iter().map(
|
||||
|ParsedField {
|
||||
options,
|
||||
vis: _,
|
||||
name,
|
||||
ty: _,
|
||||
}| {
|
||||
let FieldOptions {} = options.body;
|
||||
name
|
||||
},
|
||||
));
|
||||
let match_enum_field_name_strs = Vec::from_iter(variant.fields.iter().map(
|
||||
|ParsedField {
|
||||
options,
|
||||
vis: _,
|
||||
name,
|
||||
ty: _,
|
||||
}| {
|
||||
let FieldOptions {} = options.body;
|
||||
name.to_token_stream().to_string()
|
||||
},
|
||||
));
|
||||
let match_enum_debug_vars = Vec::from_iter(variant.fields.iter().map(
|
||||
|ParsedField {
|
||||
options,
|
||||
vis: _,
|
||||
name,
|
||||
ty: _,
|
||||
}| {
|
||||
let FieldOptions {} = options.body;
|
||||
format_ident!("__v_{}", name)
|
||||
},
|
||||
));
|
||||
match_enum_debug_arms.push(match variant.fields_kind {
|
||||
FieldsKind::Unit | FieldsKind::Named(_) => quote! {
|
||||
Self::#variant_name {
|
||||
#(#match_enum_field_names: ref #match_enum_debug_vars,)*
|
||||
} => f.debug_struct(#variant_name_str)
|
||||
#(.field(#match_enum_field_name_strs, #match_enum_debug_vars))*
|
||||
.finish(),
|
||||
},
|
||||
FieldsKind::Unnamed(_) => quote! {
|
||||
Self::#variant_name(
|
||||
#(ref #match_enum_debug_vars,)*
|
||||
) => f.debug_tuple(#variant_name_str)
|
||||
#(.field(#match_enum_debug_vars))*
|
||||
.finish(),
|
||||
},
|
||||
});
|
||||
if let Some(value_ty) = variant.value.value_ty() {
|
||||
from_canonical_type_variant_lets.push(quote! {
|
||||
let #variant_var =
|
||||
#variant_var.from_canonical_type_helper_has_value(#variant_name_str);
|
||||
});
|
||||
non_empty_variant_vars.push(variant_var.clone());
|
||||
enum_type_variants.push(quote! {
|
||||
::fayalite::enum_::VariantType {
|
||||
name: ::fayalite::intern::Intern::intern(#variant_name_str),
|
||||
ty: ::fayalite::__std::option::Option::Some(
|
||||
::fayalite::ty::DynType::canonical_dyn(&self.#variant_name),
|
||||
),
|
||||
}
|
||||
});
|
||||
enum_type_variants_hint.push(quote! {
|
||||
::fayalite::enum_::VariantType {
|
||||
name: ::fayalite::intern::Intern::intern(#variant_name_str),
|
||||
ty: ::fayalite::__std::option::Option::Some(
|
||||
::fayalite::bundle::TypeHint::<
|
||||
<#value_ty as ::fayalite::expr::ToExpr>::Type,
|
||||
>::intern_dyn(),
|
||||
),
|
||||
}
|
||||
});
|
||||
} else {
|
||||
from_canonical_type_variant_lets.push(quote! {
|
||||
#variant_var.from_canonical_type_helper_no_value(#variant_name_str);
|
||||
});
|
||||
enum_type_variants.push(quote! {
|
||||
::fayalite::enum_::VariantType {
|
||||
name: ::fayalite::intern::Intern::intern(#variant_name_str),
|
||||
ty: ::fayalite::__std::option::Option::None,
|
||||
}
|
||||
});
|
||||
enum_type_variants_hint.push(quote! {
|
||||
::fayalite::enum_::VariantType {
|
||||
name: ::fayalite::intern::Intern::intern(#variant_name_str),
|
||||
ty: ::fayalite::__std::option::Option::None,
|
||||
}
|
||||
});
|
||||
}
|
||||
variant_vars.push(variant_var);
|
||||
match_enum_arms.push(match &variant.value {
|
||||
VariantValue::None => quote! {
|
||||
#variant_index => #match_enum_ident::#variant_name,
|
||||
},
|
||||
VariantValue::Direct { value_type } => quote! {
|
||||
#variant_index => #match_enum_ident::#variant_name {
|
||||
#(#match_enum_field_names)*: ::fayalite::expr::ToExpr::to_expr(
|
||||
&__variant_access.downcast_unchecked::<
|
||||
<#value_type as ::fayalite::expr::ToExpr>::Type>(),
|
||||
),
|
||||
},
|
||||
},
|
||||
VariantValue::Struct {
|
||||
value_struct: ItemStruct { ident, .. },
|
||||
..
|
||||
} => quote! {
|
||||
#variant_index => {
|
||||
let __variant_access = ::fayalite::expr::ToExpr::to_expr(
|
||||
&__variant_access.downcast_unchecked::<<
|
||||
#ident #static_type_type_generics
|
||||
as ::fayalite::expr::ToExpr
|
||||
>::Type>(),
|
||||
);
|
||||
#match_enum_ident::#variant_name {
|
||||
#(#match_enum_field_names:
|
||||
(*__variant_access).#match_enum_field_names,)*
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
let builder_field_and_types = Vec::from_iter(variant.fields.iter().map(
|
||||
|ParsedField {
|
||||
options,
|
||||
vis: _,
|
||||
name,
|
||||
ty,
|
||||
}| {
|
||||
let FieldOptions {} = options.body;
|
||||
(name, ty)
|
||||
},
|
||||
));
|
||||
let builder_field_vars = Vec::from_iter(
|
||||
builder_field_and_types
|
||||
.iter()
|
||||
.map(|(name, _)| &builder.get_field(name).unwrap().1.builder_field_name),
|
||||
);
|
||||
let build_body = match &variant.value {
|
||||
VariantValue::None => parse_quote! {
|
||||
{
|
||||
::fayalite::expr::ToExpr::to_expr(
|
||||
&::fayalite::expr::ops::EnumLiteral::<
|
||||
#type_struct_ident #static_type_type_generics
|
||||
>::new_unchecked(
|
||||
::fayalite::__std::option::Option::None,
|
||||
#variant_index,
|
||||
::fayalite::ty::StaticType::static_type(),
|
||||
),
|
||||
)
|
||||
}
|
||||
},
|
||||
VariantValue::Direct { value_type: _ } => parse_quote! {
|
||||
{
|
||||
::fayalite::expr::ToExpr::to_expr(
|
||||
&::fayalite::expr::ops::EnumLiteral::<
|
||||
#type_struct_ident #static_type_type_generics
|
||||
>::new_unchecked(
|
||||
::fayalite::__std::option::Option::Some(
|
||||
#(#builder_field_vars)*.to_canonical_dyn(),
|
||||
),
|
||||
#variant_index,
|
||||
::fayalite::ty::StaticType::static_type(),
|
||||
),
|
||||
)
|
||||
}
|
||||
},
|
||||
VariantValue::Struct {
|
||||
parsed_struct:
|
||||
ParsedStruct {
|
||||
names:
|
||||
ParsedStructNames {
|
||||
type_struct_ident: field_type_struct_ident,
|
||||
..
|
||||
},
|
||||
..
|
||||
},
|
||||
..
|
||||
} => parse_quote! {
|
||||
{
|
||||
let __builder = <
|
||||
#field_type_struct_ident #static_type_type_generics
|
||||
as ::fayalite::bundle::BundleType
|
||||
>::builder();
|
||||
#(let __builder = __builder.#builder_field_vars(#builder_field_vars);)*
|
||||
::fayalite::expr::ToExpr::to_expr(
|
||||
&::fayalite::expr::ops::EnumLiteral::<
|
||||
#type_struct_ident #static_type_type_generics
|
||||
>::new_unchecked(
|
||||
::fayalite::__std::option::Option::Some(
|
||||
__builder.build().to_canonical_dyn(),
|
||||
),
|
||||
#variant_index,
|
||||
::fayalite::ty::StaticType::static_type(),
|
||||
),
|
||||
)
|
||||
}
|
||||
},
|
||||
};
|
||||
builder
|
||||
.make_build_method(
|
||||
&format_ident!("variant_{}", variant_name),
|
||||
variant.fields.iter().map(
|
||||
|ParsedField {
|
||||
options,
|
||||
vis: _,
|
||||
name,
|
||||
ty,
|
||||
}| {
|
||||
let FieldOptions {} = options.body;
|
||||
(name.clone(), parse_quote! { ::fayalite::expr::Expr<#ty> })
|
||||
},
|
||||
),
|
||||
&static_type_generics,
|
||||
&parse_quote! {#type_struct_ident #static_type_type_generics},
|
||||
&parse_quote! { ::fayalite::expr::Expr<#target #static_type_type_generics> },
|
||||
build_body,
|
||||
)
|
||||
.to_tokens(tokens);
|
||||
}
|
||||
let match_enum = ItemEnum {
|
||||
attrs: vec![parse_quote! {#[allow(non_camel_case_types)]}],
|
||||
vis: vis.clone(),
|
||||
enum_token: *enum_token,
|
||||
ident: match_enum_ident,
|
||||
generics: static_type_generics.clone(),
|
||||
brace_token: *brace_token,
|
||||
variants: match_enum_variants,
|
||||
};
|
||||
let match_enum_ident = &match_enum.ident;
|
||||
match_enum.to_tokens(tokens);
|
||||
make_connect_impl(
|
||||
*connect_inexact,
|
||||
&static_type_generics,
|
||||
type_struct_ident,
|
||||
variants.iter().flat_map(|variant| {
|
||||
variant.fields.iter().map(|field| {
|
||||
let ty = &field.ty;
|
||||
parse_quote_spanned! {field.name.span()=>
|
||||
<#ty as ::fayalite::expr::ToExpr>::Type
|
||||
}
|
||||
})
|
||||
}),
|
||||
)
|
||||
.to_tokens(tokens);
|
||||
let variant_count = variants.len();
|
||||
let empty_builder_ty = builder.ty([], Some(&parse_quote! { Self }), false);
|
||||
quote! {
|
||||
#[automatically_derived]
|
||||
impl #static_type_impl_generics ::fayalite::__std::fmt::Debug
|
||||
for #match_enum_ident #static_type_type_generics
|
||||
#static_type_where_clause
|
||||
{
|
||||
fn fmt(
|
||||
&self,
|
||||
f: &mut ::fayalite::__std::fmt::Formatter<'_>,
|
||||
) -> ::fayalite::__std::fmt::Result {
|
||||
match *self {
|
||||
#(#match_enum_debug_arms)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl #static_type_impl_generics ::fayalite::ty::StaticType
|
||||
for #type_struct_ident #static_type_type_generics
|
||||
#static_type_where_clause
|
||||
{
|
||||
fn static_type() -> Self {
|
||||
Self {
|
||||
#(#non_empty_variant_names: ::fayalite::ty::StaticType::static_type(),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn __check_field<T: ::fayalite::ty::Value>()
|
||||
where
|
||||
<T as ::fayalite::expr::ToExpr>::Type: ::fayalite::ty::Type<Value = T>,
|
||||
{}
|
||||
fn __check_fields #static_type_impl_generics(_: #target #static_type_type_generics)
|
||||
#static_type_where_clause
|
||||
{
|
||||
#(#field_checks)*
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl #static_type_impl_generics ::fayalite::__std::fmt::Debug
|
||||
for #type_struct_ident #static_type_type_generics
|
||||
#static_type_where_clause
|
||||
{
|
||||
fn fmt(
|
||||
&self,
|
||||
f: &mut ::fayalite::__std::fmt::Formatter<'_>,
|
||||
) -> ::fayalite::__std::fmt::Result {
|
||||
#debug_type_body
|
||||
}
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl #static_type_impl_generics ::fayalite::ty::Type
|
||||
for #type_struct_ident #static_type_type_generics
|
||||
#static_type_where_clause
|
||||
{
|
||||
type CanonicalType = ::fayalite::enum_::DynEnumType;
|
||||
type Value = #target #static_type_type_generics;
|
||||
type CanonicalValue = ::fayalite::enum_::DynEnum;
|
||||
type MaskType = ::fayalite::int::UIntType<1>;
|
||||
type MaskValue = ::fayalite::int::UInt<1>;
|
||||
type MatchVariant = #match_enum_ident #static_type_type_generics;
|
||||
type MatchActiveScope = ::fayalite::module::Scope;
|
||||
type MatchVariantAndInactiveScope =
|
||||
::fayalite::enum_::EnumMatchVariantAndInactiveScope<Self>;
|
||||
type MatchVariantsIter = ::fayalite::enum_::EnumMatchVariantsIter<Self>;
|
||||
fn match_variants<IO: ::fayalite::bundle::BundleValue>(
|
||||
this: ::fayalite::expr::Expr<<Self as ::fayalite::ty::Type>::Value>,
|
||||
module_builder: &mut ::fayalite::module::ModuleBuilder<
|
||||
IO,
|
||||
::fayalite::module::NormalModule,
|
||||
>,
|
||||
source_location: ::fayalite::source_location::SourceLocation,
|
||||
) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter
|
||||
where
|
||||
<IO as ::fayalite::expr::ToExpr>::Type:
|
||||
::fayalite::bundle::BundleType<Value = IO>,
|
||||
{
|
||||
module_builder.enum_match_variants_helper(this, source_location)
|
||||
}
|
||||
fn mask_type(&self) -> <Self as ::fayalite::ty::Type>::MaskType {
|
||||
::fayalite::int::UIntType::new()
|
||||
}
|
||||
fn canonical(&self) -> <Self as ::fayalite::ty::Type>::CanonicalType {
|
||||
let variants = ::fayalite::enum_::EnumType::variants(self);
|
||||
::fayalite::enum_::DynEnumType::new(variants)
|
||||
}
|
||||
fn source_location(&self) -> ::fayalite::source_location::SourceLocation {
|
||||
::fayalite::source_location::SourceLocation::caller()
|
||||
}
|
||||
fn type_enum(&self) -> ::fayalite::ty::TypeEnum {
|
||||
::fayalite::ty::TypeEnum::EnumType(::fayalite::ty::Type::canonical(self))
|
||||
}
|
||||
#[allow(non_snake_case)]
|
||||
fn from_canonical_type(t: <Self as ::fayalite::ty::Type>::CanonicalType) -> Self {
|
||||
let [#(#variant_vars),*] = *::fayalite::enum_::EnumType::variants(&t) else {
|
||||
::fayalite::__std::panic!("wrong number of variants");
|
||||
};
|
||||
#(#from_canonical_type_variant_lets)*
|
||||
Self {
|
||||
#(#non_empty_variant_names: #non_empty_variant_vars,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
#[allow(clippy::init_numbered_fields)]
|
||||
impl #static_type_impl_generics ::fayalite::enum_::EnumType
|
||||
for #type_struct_ident #static_type_type_generics
|
||||
#static_type_where_clause
|
||||
{
|
||||
type Builder = #empty_builder_ty;
|
||||
fn match_activate_scope(
|
||||
v: <Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope,
|
||||
) -> (
|
||||
<Self as ::fayalite::ty::Type>::MatchVariant,
|
||||
<Self as ::fayalite::ty::Type>::MatchActiveScope,
|
||||
) {
|
||||
let (__variant_access, __scope) = v.activate();
|
||||
(
|
||||
match ::fayalite::expr::ops::VariantAccess::variant_index(
|
||||
&*__variant_access,
|
||||
) {
|
||||
#(#match_enum_arms)*
|
||||
#variant_count.. => ::fayalite::__std::panic!("invalid variant index"),
|
||||
},
|
||||
__scope,
|
||||
)
|
||||
}
|
||||
fn builder() -> <Self as ::fayalite::enum_::EnumType>::Builder {
|
||||
#empty_builder_ty::new()
|
||||
}
|
||||
fn variants(&self) -> ::fayalite::intern::Interned<[::fayalite::enum_::VariantType<
|
||||
::fayalite::intern::Interned<dyn ::fayalite::ty::DynCanonicalType>,
|
||||
>]> {
|
||||
::fayalite::intern::Intern::intern(&[#(#enum_type_variants,)*][..])
|
||||
}
|
||||
fn variants_hint() -> ::fayalite::enum_::VariantsHint {
|
||||
::fayalite::enum_::VariantsHint::new([#(#enum_type_variants_hint,)*], false)
|
||||
}
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl #static_type_impl_generics ::fayalite::expr::ToExpr
|
||||
for #target #static_type_type_generics
|
||||
#static_type_where_clause
|
||||
{
|
||||
type Type = #type_struct_ident #static_type_type_generics;
|
||||
fn ty(&self) -> <Self as ::fayalite::expr::ToExpr>::Type {
|
||||
::fayalite::ty::StaticType::static_type()
|
||||
}
|
||||
fn to_expr(&self) -> ::fayalite::expr::Expr<Self> {
|
||||
::fayalite::expr::Expr::from_value(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl #static_type_impl_generics ::fayalite::ty::Value
|
||||
for #target #static_type_type_generics
|
||||
#static_type_where_clause
|
||||
{
|
||||
fn to_canonical(&self) -> <
|
||||
<Self as ::fayalite::expr::ToExpr>::Type
|
||||
as ::fayalite::ty::Type
|
||||
>::CanonicalValue
|
||||
{
|
||||
let __ty = ::fayalite::ty::Type::canonical(&::fayalite::expr::ToExpr::ty(self));
|
||||
match self {
|
||||
#(Self::#variant_names { #variant_field_pats } => {
|
||||
::fayalite::enum_::DynEnum::new_by_name(
|
||||
__ty,
|
||||
::fayalite::intern::Intern::intern(#variant_name_strs),
|
||||
#variant_to_canonical_values,
|
||||
)
|
||||
})*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl #static_type_impl_generics ::fayalite::enum_::EnumValue
|
||||
for #target #static_type_type_generics
|
||||
#static_type_where_clause
|
||||
{
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn value_derive_enum(item: ItemEnum) -> syn::Result<TokenStream> {
|
||||
let item = ParsedEnum::parse(item)?;
|
||||
let outline_generated = item.options.body.outline_generated;
|
||||
let mut contents = quote! {
|
||||
const _: () = {
|
||||
#item
|
||||
};
|
||||
};
|
||||
if outline_generated.is_some() {
|
||||
contents = crate::outline_generated(contents, "value-enum-");
|
||||
}
|
||||
Ok(contents)
|
||||
}
|
765
crates/fayalite-proc-macros-impl/src/value_derive_struct.rs
Normal file
765
crates/fayalite-proc-macros-impl/src/value_derive_struct.rs
Normal file
|
@ -0,0 +1,765 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
value_derive_common::{
|
||||
append_field, derive_clone_hash_eq_partialeq_for_struct, get_target, make_connect_impl,
|
||||
Bounds, Builder, FieldsKind, ParsedField, ValueDeriveGenerics,
|
||||
},
|
||||
Errors, HdlAttr,
|
||||
};
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::{format_ident, quote, quote_spanned, ToTokens};
|
||||
use syn::{
|
||||
parse_quote, parse_quote_spanned, spanned::Spanned, FieldMutability, Generics, Ident,
|
||||
ItemStruct, Member, Path, Token, Visibility,
|
||||
};
|
||||
|
||||
crate::options! {
|
||||
#[options = StructOptions]
|
||||
pub(crate) enum StructOption {
|
||||
OutlineGenerated(outline_generated),
|
||||
Static(static_),
|
||||
ConnectInexact(connect_inexact),
|
||||
Bounds(where_, Bounds),
|
||||
Target(target, Path),
|
||||
}
|
||||
}
|
||||
|
||||
crate::options! {
|
||||
#[options = FieldOptions]
|
||||
pub(crate) enum FieldOption {
|
||||
Flip(flip),
|
||||
Skip(skip),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ParsedStructNames<I, S> {
|
||||
pub(crate) ident: Ident,
|
||||
pub(crate) type_struct_debug_ident: S,
|
||||
pub(crate) type_struct_ident: Ident,
|
||||
pub(crate) match_variant_ident: I,
|
||||
pub(crate) builder_struct_ident: I,
|
||||
pub(crate) mask_match_variant_ident: I,
|
||||
pub(crate) mask_type_ident: I,
|
||||
pub(crate) mask_type_debug_ident: S,
|
||||
pub(crate) mask_value_ident: I,
|
||||
pub(crate) mask_value_debug_ident: S,
|
||||
pub(crate) mask_builder_struct_ident: I,
|
||||
}
|
||||
|
||||
pub(crate) struct ParsedStruct {
|
||||
pub(crate) options: HdlAttr<StructOptions>,
|
||||
pub(crate) vis: Visibility,
|
||||
pub(crate) struct_token: Token![struct],
|
||||
pub(crate) generics: Generics,
|
||||
pub(crate) fields_kind: FieldsKind,
|
||||
pub(crate) fields: Vec<ParsedField<FieldOptions>>,
|
||||
pub(crate) semi_token: Option<Token![;]>,
|
||||
pub(crate) skip_check_fields: bool,
|
||||
pub(crate) names: ParsedStructNames<Option<Ident>, Option<String>>,
|
||||
}
|
||||
|
||||
impl ParsedStruct {
|
||||
pub(crate) fn parse(item: &mut ItemStruct) -> syn::Result<Self> {
|
||||
let ItemStruct {
|
||||
attrs,
|
||||
vis,
|
||||
struct_token,
|
||||
ident,
|
||||
generics,
|
||||
fields,
|
||||
semi_token,
|
||||
} = item;
|
||||
let mut errors = Errors::new();
|
||||
let struct_options = errors
|
||||
.unwrap_or_default(HdlAttr::parse_and_take_attr(attrs))
|
||||
.unwrap_or_default();
|
||||
let (fields_kind, fields) = ParsedField::parse_fields(&mut errors, fields, false);
|
||||
errors.finish()?;
|
||||
Ok(ParsedStruct {
|
||||
options: struct_options,
|
||||
vis: vis.clone(),
|
||||
struct_token: *struct_token,
|
||||
generics: generics.clone(),
|
||||
fields_kind,
|
||||
fields,
|
||||
semi_token: *semi_token,
|
||||
skip_check_fields: false,
|
||||
names: ParsedStructNames {
|
||||
ident: ident.clone(),
|
||||
type_struct_debug_ident: None,
|
||||
type_struct_ident: format_ident!("__{}__Type", ident),
|
||||
match_variant_ident: None,
|
||||
builder_struct_ident: None,
|
||||
mask_match_variant_ident: None,
|
||||
mask_type_ident: None,
|
||||
mask_type_debug_ident: None,
|
||||
mask_value_ident: None,
|
||||
mask_value_debug_ident: None,
|
||||
mask_builder_struct_ident: None,
|
||||
},
|
||||
})
|
||||
}
|
||||
pub(crate) fn write_body(
|
||||
&self,
|
||||
target: Path,
|
||||
names: ParsedStructNames<&Ident, &String>,
|
||||
is_for_mask: bool,
|
||||
tokens: &mut TokenStream,
|
||||
) {
|
||||
let Self {
|
||||
options,
|
||||
vis,
|
||||
struct_token,
|
||||
generics,
|
||||
fields_kind,
|
||||
fields,
|
||||
semi_token,
|
||||
skip_check_fields,
|
||||
names: _,
|
||||
} = self;
|
||||
let skip_check_fields = *skip_check_fields || is_for_mask;
|
||||
let ParsedStructNames {
|
||||
ident: struct_ident,
|
||||
type_struct_debug_ident,
|
||||
type_struct_ident,
|
||||
match_variant_ident,
|
||||
builder_struct_ident,
|
||||
mask_match_variant_ident: _,
|
||||
mask_type_ident,
|
||||
mask_type_debug_ident: _,
|
||||
mask_value_ident,
|
||||
mask_value_debug_ident,
|
||||
mask_builder_struct_ident: _,
|
||||
} = names;
|
||||
let StructOptions {
|
||||
outline_generated: _,
|
||||
where_,
|
||||
target: _,
|
||||
static_,
|
||||
connect_inexact,
|
||||
} = &options.body;
|
||||
let ValueDeriveGenerics {
|
||||
generics,
|
||||
static_type_generics,
|
||||
} = ValueDeriveGenerics::get(generics.clone(), where_);
|
||||
let (impl_generics, type_generics, where_clause) = generics.split_for_impl();
|
||||
let unskipped_fields = fields
|
||||
.iter()
|
||||
.filter(|field| field.options.body.skip.is_none());
|
||||
let _field_names = Vec::from_iter(fields.iter().map(|field| field.name.clone()));
|
||||
let unskipped_field_names =
|
||||
Vec::from_iter(unskipped_fields.clone().map(|field| field.name.clone()));
|
||||
let unskipped_field_name_strs = Vec::from_iter(
|
||||
unskipped_field_names
|
||||
.iter()
|
||||
.map(|field_name| field_name.to_token_stream().to_string()),
|
||||
);
|
||||
let unskipped_field_vars = Vec::from_iter(
|
||||
unskipped_field_names
|
||||
.iter()
|
||||
.map(|field_name| format_ident!("__v_{}", field_name)),
|
||||
);
|
||||
let unskipped_field_flips = Vec::from_iter(
|
||||
unskipped_fields
|
||||
.clone()
|
||||
.map(|field| field.options.body.flip.is_some()),
|
||||
);
|
||||
let mut any_fields_skipped = false;
|
||||
let type_fields = Vec::from_iter(fields.iter().filter_map(|field| {
|
||||
let ParsedField {
|
||||
options,
|
||||
vis,
|
||||
name,
|
||||
ty,
|
||||
} = field;
|
||||
let FieldOptions { flip: _, skip } = &options.body;
|
||||
if skip.is_some() {
|
||||
any_fields_skipped = true;
|
||||
return None;
|
||||
}
|
||||
let ty = if is_for_mask {
|
||||
parse_quote! { ::fayalite::ty::AsMask<#ty> }
|
||||
} else {
|
||||
ty.to_token_stream()
|
||||
};
|
||||
Some(syn::Field {
|
||||
attrs: vec![],
|
||||
vis: vis.clone(),
|
||||
mutability: FieldMutability::None,
|
||||
ident: match name.clone() {
|
||||
Member::Named(name) => Some(name),
|
||||
Member::Unnamed(_) => None,
|
||||
},
|
||||
colon_token: None,
|
||||
ty: parse_quote! { <#ty as ::fayalite::expr::ToExpr>::Type },
|
||||
})
|
||||
}));
|
||||
let field_types = Vec::from_iter(type_fields.iter().map(|field| field.ty.clone()));
|
||||
let match_variant_fields = Vec::from_iter(fields.iter().zip(&type_fields).map(
|
||||
|(parsed_field, type_field)| {
|
||||
let field_ty = &parsed_field.ty;
|
||||
syn::Field {
|
||||
ty: parse_quote! { ::fayalite::expr::Expr<#field_ty> },
|
||||
..type_field.clone()
|
||||
}
|
||||
},
|
||||
));
|
||||
|
||||
let mask_value_fields = Vec::from_iter(fields.iter().zip(&type_fields).map(
|
||||
|(parsed_field, type_field)| {
|
||||
let field_ty = &parsed_field.ty;
|
||||
syn::Field {
|
||||
ty: parse_quote! { ::fayalite::ty::AsMask<#field_ty> },
|
||||
..type_field.clone()
|
||||
}
|
||||
},
|
||||
));
|
||||
|
||||
let mut type_struct_fields = fields_kind.into_fields(type_fields);
|
||||
let mut match_variant_fields = fields_kind.into_fields(match_variant_fields);
|
||||
let mut mask_value_fields = fields_kind.into_fields(mask_value_fields);
|
||||
let phantom_data_field_name = any_fields_skipped.then(|| {
|
||||
let phantom_data_field_name = Ident::new("__phantom_data", type_struct_ident.span());
|
||||
let member = append_field(
|
||||
&mut type_struct_fields,
|
||||
syn::Field {
|
||||
attrs: vec![],
|
||||
vis: vis.clone(),
|
||||
mutability: FieldMutability::None,
|
||||
ident: Some(phantom_data_field_name.clone()),
|
||||
colon_token: None,
|
||||
ty: parse_quote_spanned! {type_struct_ident.span()=>
|
||||
::fayalite::__std::marker::PhantomData<#struct_ident #type_generics>
|
||||
},
|
||||
},
|
||||
);
|
||||
append_field(
|
||||
&mut match_variant_fields,
|
||||
syn::Field {
|
||||
attrs: vec![],
|
||||
vis: Visibility::Inherited,
|
||||
mutability: FieldMutability::None,
|
||||
ident: Some(phantom_data_field_name.clone()),
|
||||
colon_token: None,
|
||||
ty: parse_quote_spanned! {type_struct_ident.span()=>
|
||||
::fayalite::__std::marker::PhantomData<#struct_ident #type_generics>
|
||||
},
|
||||
},
|
||||
);
|
||||
append_field(
|
||||
&mut mask_value_fields,
|
||||
syn::Field {
|
||||
attrs: vec![],
|
||||
vis: Visibility::Inherited,
|
||||
mutability: FieldMutability::None,
|
||||
ident: Some(phantom_data_field_name),
|
||||
colon_token: None,
|
||||
ty: parse_quote_spanned! {type_struct_ident.span()=>
|
||||
::fayalite::__std::marker::PhantomData<#struct_ident #type_generics>
|
||||
},
|
||||
},
|
||||
);
|
||||
member
|
||||
});
|
||||
let phantom_data_field_name_slice = phantom_data_field_name.as_slice();
|
||||
let type_struct = ItemStruct {
|
||||
attrs: vec![parse_quote! {#[allow(non_camel_case_types)]}],
|
||||
vis: vis.clone(),
|
||||
struct_token: *struct_token,
|
||||
ident: type_struct_ident.clone(),
|
||||
generics: generics.clone(),
|
||||
fields: type_struct_fields,
|
||||
semi_token: *semi_token,
|
||||
};
|
||||
type_struct.to_tokens(tokens);
|
||||
let match_variant_struct = ItemStruct {
|
||||
attrs: vec![parse_quote! {#[allow(non_camel_case_types)]}],
|
||||
vis: vis.clone(),
|
||||
struct_token: *struct_token,
|
||||
ident: match_variant_ident.clone(),
|
||||
generics: generics.clone(),
|
||||
fields: match_variant_fields,
|
||||
semi_token: *semi_token,
|
||||
};
|
||||
match_variant_struct.to_tokens(tokens);
|
||||
let mask_type_body = if is_for_mask {
|
||||
quote! {
|
||||
::fayalite::__std::clone::Clone::clone(self)
|
||||
}
|
||||
} else {
|
||||
let mask_value_struct = ItemStruct {
|
||||
attrs: vec![parse_quote! {#[allow(non_camel_case_types)]}],
|
||||
vis: vis.clone(),
|
||||
struct_token: *struct_token,
|
||||
ident: mask_value_ident.clone(),
|
||||
generics: generics.clone(),
|
||||
fields: mask_value_fields,
|
||||
semi_token: *semi_token,
|
||||
};
|
||||
mask_value_struct.to_tokens(tokens);
|
||||
let debug_mask_value_body = match fields_kind {
|
||||
FieldsKind::Unit => quote! {
|
||||
f.debug_struct(#mask_value_debug_ident).finish()
|
||||
},
|
||||
FieldsKind::Named(_) => quote! {
|
||||
f.debug_struct(#mask_value_debug_ident)
|
||||
#(.field(#unskipped_field_name_strs, &self.#unskipped_field_names))*
|
||||
.finish()
|
||||
},
|
||||
FieldsKind::Unnamed(_) => quote! {
|
||||
f.debug_tuple(#mask_value_debug_ident)
|
||||
#(.field(&self.#unskipped_field_names))*
|
||||
.finish()
|
||||
},
|
||||
};
|
||||
quote! {
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::__std::fmt::Debug
|
||||
for #mask_value_ident #type_generics
|
||||
#where_clause
|
||||
{
|
||||
fn fmt(
|
||||
&self,
|
||||
f: &mut ::fayalite::__std::fmt::Formatter<'_>,
|
||||
) -> ::fayalite::__std::fmt::Result {
|
||||
#debug_mask_value_body
|
||||
}
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
quote! {
|
||||
#mask_type_ident {
|
||||
#(#unskipped_field_names:
|
||||
::fayalite::ty::Type::mask_type(&self.#unskipped_field_names),)*
|
||||
#(#phantom_data_field_name_slice: ::fayalite::__std::marker::PhantomData,)*
|
||||
}
|
||||
}
|
||||
};
|
||||
let debug_type_body = match fields_kind {
|
||||
FieldsKind::Unit => quote! {
|
||||
f.debug_struct(#type_struct_debug_ident).finish()
|
||||
},
|
||||
FieldsKind::Named(_) => quote! {
|
||||
f.debug_struct(#type_struct_debug_ident)
|
||||
#(.field(#unskipped_field_name_strs, &self.#unskipped_field_names))*
|
||||
.finish()
|
||||
},
|
||||
FieldsKind::Unnamed(_) => quote! {
|
||||
f.debug_tuple(#type_struct_debug_ident)
|
||||
#(.field(&self.#unskipped_field_names))*
|
||||
.finish()
|
||||
},
|
||||
};
|
||||
for the_struct_ident in [&type_struct_ident, match_variant_ident]
|
||||
.into_iter()
|
||||
.chain(is_for_mask.then_some(mask_value_ident))
|
||||
{
|
||||
derive_clone_hash_eq_partialeq_for_struct(
|
||||
the_struct_ident,
|
||||
&generics,
|
||||
&Vec::from_iter(
|
||||
unskipped_field_names
|
||||
.iter()
|
||||
.cloned()
|
||||
.chain(phantom_data_field_name.clone()),
|
||||
),
|
||||
)
|
||||
.to_tokens(tokens);
|
||||
}
|
||||
let check_v = format_ident!("__v");
|
||||
let field_checks = Vec::from_iter(fields.iter().map(|ParsedField { ty, name, .. }| {
|
||||
quote_spanned! {ty.span()=>
|
||||
__check_field(#check_v.#name);
|
||||
}
|
||||
}));
|
||||
if static_.is_some() {
|
||||
let (impl_generics, type_generics, where_clause) =
|
||||
static_type_generics.split_for_impl();
|
||||
quote! {
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::ty::StaticType for #type_struct_ident #type_generics
|
||||
#where_clause
|
||||
{
|
||||
fn static_type() -> Self {
|
||||
Self {
|
||||
#(#unskipped_field_names: ::fayalite::ty::StaticType::static_type(),)*
|
||||
#(#phantom_data_field_name_slice:
|
||||
::fayalite::__std::marker::PhantomData,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
}
|
||||
if !skip_check_fields {
|
||||
quote! {
|
||||
fn __check_field<T: ::fayalite::ty::Value>(_v: T)
|
||||
where
|
||||
<T as ::fayalite::expr::ToExpr>::Type: ::fayalite::ty::Type<Value = T>,
|
||||
{}
|
||||
fn __check_fields #impl_generics(#check_v: #target #type_generics)
|
||||
#where_clause
|
||||
{
|
||||
#(#field_checks)*
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
}
|
||||
let mut builder = Builder::new(builder_struct_ident.clone(), vis.clone());
|
||||
for field in unskipped_fields.clone() {
|
||||
builder.insert_field(
|
||||
field.name.clone(),
|
||||
|v| {
|
||||
parse_quote_spanned! {v.span()=>
|
||||
::fayalite::expr::ToExpr::to_expr(&#v)
|
||||
}
|
||||
},
|
||||
|t| {
|
||||
parse_quote_spanned! {t.span()=>
|
||||
::fayalite::expr::Expr<<
|
||||
<#t as ::fayalite::expr::ToExpr>::Type
|
||||
as ::fayalite::ty::Type
|
||||
>::Value>
|
||||
}
|
||||
},
|
||||
|t| {
|
||||
parse_quote_spanned! {t.span()=>
|
||||
where
|
||||
#t: ::fayalite::expr::ToExpr,
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
let builder = builder.finish_filling_in_fields();
|
||||
builder.to_tokens(tokens);
|
||||
let build_type_fields =
|
||||
Vec::from_iter(unskipped_fields.clone().map(|ParsedField { name, .. }| {
|
||||
let builder_field_name = &builder.get_field(name).unwrap().1.builder_field_name;
|
||||
quote_spanned! {struct_ident.span()=>
|
||||
#name: ::fayalite::expr::ToExpr::ty(&#builder_field_name)
|
||||
}
|
||||
}));
|
||||
let build_expr_fields =
|
||||
Vec::from_iter(unskipped_fields.clone().map(|ParsedField { name, .. }| {
|
||||
let builder_field_name = &builder.get_field(name).unwrap().1.builder_field_name;
|
||||
quote_spanned! {struct_ident.span()=>
|
||||
#builder_field_name.to_canonical_dyn()
|
||||
}
|
||||
}));
|
||||
let build_specified_fields = unskipped_fields.clone().map(
|
||||
|ParsedField {
|
||||
options: _,
|
||||
vis: _,
|
||||
name,
|
||||
ty,
|
||||
}| {
|
||||
let ty = if is_for_mask {
|
||||
parse_quote_spanned! {name.span()=>
|
||||
::fayalite::expr::Expr<::fayalite::ty::AsMask<#ty>>
|
||||
}
|
||||
} else {
|
||||
parse_quote_spanned! {name.span()=>
|
||||
::fayalite::expr::Expr<#ty>
|
||||
}
|
||||
};
|
||||
(name.clone(), ty)
|
||||
},
|
||||
);
|
||||
let build_body = parse_quote_spanned! {struct_ident.span()=>
|
||||
{
|
||||
::fayalite::expr::ToExpr::to_expr(
|
||||
&::fayalite::expr::ops::BundleLiteral::new_unchecked(
|
||||
::fayalite::intern::Intern::intern(&[#(
|
||||
#build_expr_fields,
|
||||
)*][..]),
|
||||
#type_struct_ident {
|
||||
#(#build_type_fields,)*
|
||||
#(#phantom_data_field_name_slice:
|
||||
::fayalite::__std::marker::PhantomData,)*
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
};
|
||||
builder
|
||||
.make_build_method(
|
||||
&Ident::new("build", struct_ident.span()),
|
||||
build_specified_fields,
|
||||
&generics,
|
||||
&parse_quote_spanned! {struct_ident.span()=>
|
||||
#type_struct_ident #type_generics
|
||||
},
|
||||
&parse_quote_spanned! {struct_ident.span()=>
|
||||
::fayalite::expr::Expr<#target #type_generics>
|
||||
},
|
||||
build_body,
|
||||
)
|
||||
.to_tokens(tokens);
|
||||
make_connect_impl(
|
||||
*connect_inexact,
|
||||
&generics,
|
||||
&type_struct_ident,
|
||||
unskipped_fields.clone().map(|field| {
|
||||
let ty = &field.ty;
|
||||
parse_quote_spanned! {field.name.span()=>
|
||||
<#ty as ::fayalite::expr::ToExpr>::Type
|
||||
}
|
||||
}),
|
||||
)
|
||||
.to_tokens(tokens);
|
||||
let empty_builder_ty = builder.ty([], Some(&parse_quote! { Self }), false);
|
||||
quote! {
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::__std::fmt::Debug for #type_struct_ident #type_generics
|
||||
#where_clause
|
||||
{
|
||||
fn fmt(
|
||||
&self,
|
||||
f: &mut ::fayalite::__std::fmt::Formatter<'_>,
|
||||
) -> ::fayalite::__std::fmt::Result {
|
||||
#debug_type_body
|
||||
}
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::ty::Type for #type_struct_ident #type_generics
|
||||
#where_clause
|
||||
{
|
||||
type CanonicalType = ::fayalite::bundle::DynBundleType;
|
||||
type Value = #target #type_generics;
|
||||
type CanonicalValue = ::fayalite::bundle::DynBundle;
|
||||
type MaskType = #mask_type_ident #type_generics;
|
||||
type MaskValue = #mask_value_ident #type_generics;
|
||||
type MatchVariant = #match_variant_ident #type_generics;
|
||||
type MatchActiveScope = ();
|
||||
type MatchVariantAndInactiveScope = ::fayalite::ty::MatchVariantWithoutScope<
|
||||
#match_variant_ident #type_generics,
|
||||
>;
|
||||
type MatchVariantsIter = ::fayalite::__std::iter::Once<
|
||||
<Self as ::fayalite::ty::Type>::MatchVariantAndInactiveScope,
|
||||
>;
|
||||
#[allow(unused_variables)]
|
||||
fn match_variants<IO: ::fayalite::bundle::BundleValue>(
|
||||
this: ::fayalite::expr::Expr<<Self as ::fayalite::ty::Type>::Value>,
|
||||
module_builder: &mut ::fayalite::module::ModuleBuilder<
|
||||
IO,
|
||||
::fayalite::module::NormalModule,
|
||||
>,
|
||||
source_location: ::fayalite::source_location::SourceLocation,
|
||||
) -> <Self as ::fayalite::ty::Type>::MatchVariantsIter
|
||||
where
|
||||
<IO as ::fayalite::expr::ToExpr>::Type:
|
||||
::fayalite::bundle::BundleType<Value = IO>,
|
||||
{
|
||||
::fayalite::__std::iter::once(::fayalite::ty::MatchVariantWithoutScope(
|
||||
#match_variant_ident {
|
||||
#(#unskipped_field_names: this.field(#unskipped_field_name_strs),)*
|
||||
#(#phantom_data_field_name_slice:
|
||||
::fayalite::__std::marker::PhantomData,)*
|
||||
},
|
||||
))
|
||||
}
|
||||
fn mask_type(&self) -> <Self as ::fayalite::ty::Type>::MaskType {
|
||||
#mask_type_body
|
||||
}
|
||||
fn canonical(&self) -> <Self as ::fayalite::ty::Type>::CanonicalType {
|
||||
let fields = ::fayalite::bundle::BundleType::fields(self);
|
||||
::fayalite::bundle::DynBundleType::new(fields)
|
||||
}
|
||||
fn source_location(&self) -> ::fayalite::source_location::SourceLocation {
|
||||
::fayalite::source_location::SourceLocation::caller()
|
||||
}
|
||||
fn type_enum(&self) -> ::fayalite::ty::TypeEnum {
|
||||
::fayalite::ty::TypeEnum::BundleType(::fayalite::ty::Type::canonical(self))
|
||||
}
|
||||
fn from_canonical_type(t: <Self as ::fayalite::ty::Type>::CanonicalType) -> Self {
|
||||
let [#(#unskipped_field_vars),*] = *::fayalite::bundle::BundleType::fields(&t)
|
||||
else {
|
||||
::fayalite::__std::panic!("wrong number of fields");
|
||||
};
|
||||
Self {
|
||||
#(#unskipped_field_names: #unskipped_field_vars.from_canonical_type_helper(
|
||||
#unskipped_field_name_strs,
|
||||
#unskipped_field_flips,
|
||||
),)*
|
||||
#(#phantom_data_field_name_slice: ::fayalite::__std::marker::PhantomData,)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::ty::TypeWithDeref for #type_struct_ident #type_generics
|
||||
#where_clause
|
||||
{
|
||||
#[allow(unused_variables)]
|
||||
fn expr_deref(this: &::fayalite::expr::Expr<<Self as ::fayalite::ty::Type>::Value>)
|
||||
-> &<Self as ::fayalite::ty::Type>::MatchVariant
|
||||
{
|
||||
::fayalite::intern::Interned::<_>::into_inner(
|
||||
::fayalite::intern::Intern::intern_sized(#match_variant_ident {
|
||||
#(#unskipped_field_names: this.field(#unskipped_field_name_strs),)*
|
||||
#(#phantom_data_field_name_slice:
|
||||
::fayalite::__std::marker::PhantomData,)*
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::bundle::BundleType for #type_struct_ident #type_generics
|
||||
#where_clause
|
||||
{
|
||||
type Builder = #empty_builder_ty;
|
||||
fn builder() -> <Self as ::fayalite::bundle::BundleType>::Builder {
|
||||
#empty_builder_ty::new()
|
||||
}
|
||||
fn fields(&self) -> ::fayalite::intern::Interned<
|
||||
[::fayalite::bundle::FieldType<::fayalite::intern::Interned<
|
||||
dyn ::fayalite::ty::DynCanonicalType,
|
||||
>>],
|
||||
>
|
||||
{
|
||||
::fayalite::intern::Intern::intern(&[#(
|
||||
::fayalite::bundle::FieldType {
|
||||
name: ::fayalite::intern::Intern::intern(#unskipped_field_name_strs),
|
||||
flipped: #unskipped_field_flips,
|
||||
ty: ::fayalite::ty::DynType::canonical_dyn(
|
||||
&self.#unskipped_field_names,
|
||||
),
|
||||
},
|
||||
)*][..])
|
||||
}
|
||||
fn fields_hint() -> ::fayalite::bundle::FieldsHint {
|
||||
::fayalite::bundle::FieldsHint::new([#(
|
||||
::fayalite::bundle::FieldType {
|
||||
name: ::fayalite::intern::Intern::intern(#unskipped_field_name_strs),
|
||||
flipped: #unskipped_field_flips,
|
||||
ty: ::fayalite::bundle::TypeHint::<#field_types>::intern_dyn(),
|
||||
},
|
||||
)*], false)
|
||||
}
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::expr::ToExpr for #target #type_generics
|
||||
#where_clause
|
||||
{
|
||||
type Type = #type_struct_ident #type_generics;
|
||||
fn ty(&self) -> <Self as ::fayalite::expr::ToExpr>::Type {
|
||||
#type_struct_ident {
|
||||
#(#unskipped_field_names: ::fayalite::expr::ToExpr::ty(
|
||||
&self.#unskipped_field_names,
|
||||
),)*
|
||||
#(#phantom_data_field_name_slice: ::fayalite::__std::marker::PhantomData,)*
|
||||
}
|
||||
}
|
||||
fn to_expr(&self) -> ::fayalite::expr::Expr<Self> {
|
||||
::fayalite::expr::Expr::from_value(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::ty::Value for #target #type_generics
|
||||
#where_clause
|
||||
{
|
||||
fn to_canonical(&self) -> <
|
||||
<Self as ::fayalite::expr::ToExpr>::Type
|
||||
as ::fayalite::ty::Type
|
||||
>::CanonicalValue
|
||||
{
|
||||
let ty = ::fayalite::ty::Type::canonical(&::fayalite::expr::ToExpr::ty(self));
|
||||
::fayalite::bundle::DynBundle::new(ty, ::fayalite::__std::sync::Arc::new([
|
||||
#(::fayalite::ty::DynValueTrait::to_canonical_dyn(
|
||||
&self.#unskipped_field_names,
|
||||
),)*
|
||||
]))
|
||||
}
|
||||
}
|
||||
|
||||
#[automatically_derived]
|
||||
impl #impl_generics ::fayalite::bundle::BundleValue for #target #type_generics
|
||||
#where_clause
|
||||
{
|
||||
}
|
||||
}
|
||||
.to_tokens(tokens);
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for ParsedStruct {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let ParsedStructNames {
|
||||
ident: struct_ident,
|
||||
type_struct_debug_ident,
|
||||
type_struct_ident,
|
||||
match_variant_ident,
|
||||
builder_struct_ident,
|
||||
mask_match_variant_ident,
|
||||
mask_type_ident,
|
||||
mask_type_debug_ident,
|
||||
mask_value_ident,
|
||||
mask_value_debug_ident,
|
||||
mask_builder_struct_ident,
|
||||
} = &self.names;
|
||||
macro_rules! unwrap_or_set {
|
||||
($(let $var:ident =? $fallback_value:expr;)*) => {
|
||||
$(let $var = $var.clone().unwrap_or_else(|| $fallback_value);)*
|
||||
};
|
||||
}
|
||||
unwrap_or_set! {
|
||||
let type_struct_debug_ident =? format!("{struct_ident}::Type");
|
||||
let match_variant_ident =? format_ident!("__{}__MatchVariant", struct_ident);
|
||||
let builder_struct_ident =? format_ident!("__{}__Builder", struct_ident);
|
||||
let mask_match_variant_ident =? format_ident!("__AsMask__{}__MatchVariant", struct_ident);
|
||||
let mask_type_ident =? format_ident!("__AsMask__{}__Type", struct_ident);
|
||||
let mask_type_debug_ident =? format!("AsMask<{struct_ident}>::Type");
|
||||
let mask_value_ident =? format_ident!("__AsMask__{}", struct_ident);
|
||||
let mask_value_debug_ident =? format!("AsMask<{struct_ident}>");
|
||||
let mask_builder_struct_ident =? format_ident!("__AsMask__{}__Builder", struct_ident);
|
||||
}
|
||||
let target = get_target(&self.options.body.target, struct_ident);
|
||||
let names = ParsedStructNames {
|
||||
ident: struct_ident.clone(),
|
||||
type_struct_debug_ident: &type_struct_debug_ident,
|
||||
type_struct_ident: type_struct_ident.clone(),
|
||||
match_variant_ident: &match_variant_ident,
|
||||
builder_struct_ident: &builder_struct_ident,
|
||||
mask_match_variant_ident: &mask_match_variant_ident,
|
||||
mask_type_ident: &mask_type_ident,
|
||||
mask_type_debug_ident: &mask_type_debug_ident,
|
||||
mask_value_ident: &mask_value_ident,
|
||||
mask_value_debug_ident: &mask_value_debug_ident,
|
||||
mask_builder_struct_ident: &mask_builder_struct_ident,
|
||||
};
|
||||
self.write_body(target, names, false, tokens);
|
||||
let mask_names = ParsedStructNames {
|
||||
ident: mask_value_ident.clone(),
|
||||
type_struct_debug_ident: &mask_type_debug_ident,
|
||||
type_struct_ident: mask_type_ident.clone(),
|
||||
match_variant_ident: &mask_match_variant_ident,
|
||||
builder_struct_ident: &mask_builder_struct_ident,
|
||||
mask_match_variant_ident: &mask_match_variant_ident,
|
||||
mask_type_ident: &mask_type_ident,
|
||||
mask_type_debug_ident: &mask_type_debug_ident,
|
||||
mask_value_ident: &mask_value_ident,
|
||||
mask_value_debug_ident: &mask_value_debug_ident,
|
||||
mask_builder_struct_ident: &mask_builder_struct_ident,
|
||||
};
|
||||
self.write_body(mask_value_ident.clone().into(), mask_names, true, tokens);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn value_derive_struct(mut item: ItemStruct) -> syn::Result<TokenStream> {
|
||||
let item = ParsedStruct::parse(&mut item)?;
|
||||
let outline_generated = item.options.body.outline_generated;
|
||||
let mut contents = quote! {
|
||||
const _: () = {
|
||||
#item
|
||||
};
|
||||
};
|
||||
if outline_generated.is_some() {
|
||||
contents = crate::outline_generated(contents, "value-struct-");
|
||||
}
|
||||
Ok(contents)
|
||||
}
|
|
@ -16,4 +16,4 @@ version.workspace = true
|
|||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
fayalite-proc-macros-impl.workspace = true
|
||||
fayalite-proc-macros-impl = { workspace = true }
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
// See Notices.txt for copyright information
|
||||
//! proc macros for `fayalite`
|
||||
//!
|
||||
//! see `fayalite::hdl_module` and `fayalite::hdl` for docs
|
||||
//! see `fayalite::hdl_module` and `fayalite::ty::Value` for docs
|
||||
|
||||
// intentionally not documented here, see `fayalite::hdl_module` for docs
|
||||
#[proc_macro_attribute]
|
||||
|
@ -10,19 +10,16 @@ pub fn hdl_module(
|
|||
attr: proc_macro::TokenStream,
|
||||
item: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
match fayalite_proc_macros_impl::hdl_module(attr.into(), item.into()) {
|
||||
match fayalite_proc_macros_impl::module(attr.into(), item.into()) {
|
||||
Ok(retval) => retval.into(),
|
||||
Err(err) => err.into_compile_error().into(),
|
||||
}
|
||||
}
|
||||
|
||||
// intentionally not documented here, see `fayalite::hdl` for docs
|
||||
#[proc_macro_attribute]
|
||||
pub fn hdl(
|
||||
attr: proc_macro::TokenStream,
|
||||
item: proc_macro::TokenStream,
|
||||
) -> proc_macro::TokenStream {
|
||||
match fayalite_proc_macros_impl::hdl_attr(attr.into(), item.into()) {
|
||||
// intentionally not documented here, see `fayalite::ty::Value` for docs
|
||||
#[proc_macro_derive(Value, attributes(hdl))]
|
||||
pub fn value_derive(item: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||
match fayalite_proc_macros_impl::value_derive(item.into()) {
|
||||
Ok(retval) => retval.into(),
|
||||
Err(err) => err.into_compile_error().into(),
|
||||
}
|
||||
|
|
|
@ -13,11 +13,11 @@ rust-version.workspace = true
|
|||
version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
indexmap.workspace = true
|
||||
prettyplease.workspace = true
|
||||
proc-macro2.workspace = true
|
||||
quote.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
syn.workspace = true
|
||||
thiserror.workspace = true
|
||||
indexmap = { workspace = true }
|
||||
prettyplease = { workspace = true }
|
||||
proc-macro2 = { workspace = true }
|
||||
quote = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
syn = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
|
|
|
@ -14,27 +14,22 @@ rust-version.workspace = true
|
|||
version.workspace = true
|
||||
|
||||
[dependencies]
|
||||
bitvec.workspace = true
|
||||
blake3.workspace = true
|
||||
clap.workspace = true
|
||||
ctor.workspace = true
|
||||
eyre.workspace = true
|
||||
fayalite-proc-macros.workspace = true
|
||||
hashbrown.workspace = true
|
||||
jobslot.workspace = true
|
||||
num-bigint.workspace = true
|
||||
num-traits.workspace = true
|
||||
os_pipe.workspace = true
|
||||
serde_json.workspace = true
|
||||
serde.workspace = true
|
||||
tempfile.workspace = true
|
||||
which.workspace = true
|
||||
bitvec = { workspace = true }
|
||||
hashbrown = { workspace = true }
|
||||
num-bigint = { workspace = true }
|
||||
num-traits = { workspace = true }
|
||||
fayalite-proc-macros = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
clap = { version = "4.5.9", features = ["derive", "env"] }
|
||||
eyre = "0.6.12"
|
||||
which = "6.0.1"
|
||||
|
||||
[dev-dependencies]
|
||||
trybuild.workspace = true
|
||||
trybuild = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
fayalite-visit-gen.workspace = true
|
||||
fayalite-visit-gen = { workspace = true }
|
||||
|
||||
[features]
|
||||
unstable-doc = []
|
||||
|
|
|
@ -4,7 +4,6 @@ use fayalite_visit_gen::parse_and_generate;
|
|||
use std::{env, fs, path::Path};
|
||||
|
||||
fn main() {
|
||||
println!("cargo::rustc-check-cfg=cfg(todo)");
|
||||
let path = "visit_types.json";
|
||||
println!("cargo::rerun-if-changed={path}");
|
||||
println!("cargo::rerun-if-changed=build.rs");
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use clap::Parser;
|
||||
use fayalite::{cli, prelude::*};
|
||||
use fayalite::{
|
||||
cli,
|
||||
clock::{Clock, ClockDomain},
|
||||
hdl_module,
|
||||
int::{DynUInt, DynUIntType, IntCmp, IntTypeTrait, UInt},
|
||||
reset::{SyncReset, ToReset},
|
||||
};
|
||||
|
||||
#[hdl_module]
|
||||
fn blinky(clock_frequency: u64) {
|
||||
|
@ -15,21 +19,21 @@ fn blinky(clock_frequency: u64) {
|
|||
rst: rst.to_reset(),
|
||||
};
|
||||
let max_value = clock_frequency / 2 - 1;
|
||||
let int_ty = UInt::range_inclusive(0..=max_value);
|
||||
let int_ty = DynUIntType::range_inclusive(0..=max_value);
|
||||
#[hdl]
|
||||
let counter_reg: UInt = reg_builder().clock_domain(cd).reset(0u8.cast_to(int_ty));
|
||||
let counter: DynUInt = m.reg_builder().clock_domain(cd).reset(int_ty.literal(0));
|
||||
#[hdl]
|
||||
let output_reg: Bool = reg_builder().clock_domain(cd).reset(false);
|
||||
let output_reg: UInt<1> = m.reg_builder().clock_domain(cd).reset_default();
|
||||
#[hdl]
|
||||
if counter_reg.cmp_eq(max_value) {
|
||||
connect_any(counter_reg, 0u8);
|
||||
connect(output_reg, !output_reg);
|
||||
if counter.cmp_eq(max_value) {
|
||||
m.connect_any(counter, 0u8);
|
||||
m.connect(output_reg, !output_reg);
|
||||
} else {
|
||||
connect_any(counter_reg, counter_reg + 1_hdl_u1);
|
||||
m.connect_any(counter, counter + 1_hdl_u1);
|
||||
}
|
||||
#[hdl]
|
||||
let led: Bool = m.output();
|
||||
connect(led, output_reg);
|
||||
let led: UInt<1> = m.output();
|
||||
m.connect(led, output_reg);
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
//!
|
||||
|
@ -12,7 +10,7 @@
|
|||
//! function to add inputs/outputs and other components to that module.
|
||||
//!
|
||||
//! ```
|
||||
//! # use fayalite::prelude::*;
|
||||
//! # use fayalite::{hdl_module, int::UInt};
|
||||
//! #
|
||||
//! #[hdl_module]
|
||||
//! pub fn example_module() {
|
||||
|
@ -20,7 +18,7 @@
|
|||
//! let an_input: UInt<10> = m.input(); // create an input that is a 10-bit unsigned integer
|
||||
//! #[hdl]
|
||||
//! let some_output: UInt<10> = m.output();
|
||||
//! connect(some_output, an_input); // assigns the value of `an_input` to `some_output`
|
||||
//! m.connect(some_output, an_input); // assigns the value of `an_input` to `some_output`
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
//! # Fayalite Modules
|
||||
//!
|
||||
//! The [`#[hdl_module]`][`crate::hdl_module`] attribute is applied to a Rust
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
//! These are for when you want to use modules written in
|
||||
//! some other language, such as Verilog.
|
||||
//!
|
||||
|
@ -13,6 +11,8 @@
|
|||
//! * [`parameter_raw_verilog()`][`ModuleBuilder::parameter_raw_verilog`]
|
||||
//! * [`parameter()`][`ModuleBuilder::parameter`]
|
||||
//!
|
||||
//! These use the [`ExternModule`][`crate::module::ExternModule`] tag type.
|
||||
//!
|
||||
//! [inputs/outputs]: crate::_docs::modules::module_bodies::hdl_let_statements::inputs_outputs
|
||||
|
||||
#[allow(unused)]
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
//! # Module Function Bodies
|
||||
//!
|
||||
//! The `#[hdl_module]` attribute lets you have statements/expressions with `#[hdl]` annotations
|
||||
|
|
|
@ -1,26 +1,24 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
//! # `#[hdl]` Array Expressions
|
||||
//!
|
||||
//! `#[hdl]` can be used on Array Expressions to construct an [`Array<[T; N]>`][type@Array] expression:
|
||||
//! `#[hdl]` can be used on Array Expressions to construct an [`Array<[T; N]>`][Array] expression:
|
||||
//!
|
||||
//! ```
|
||||
//! # use fayalite::prelude::*;
|
||||
//! # use fayalite::{hdl_module, int::UInt, array::Array};
|
||||
//! # #[hdl_module]
|
||||
//! # fn module() {
|
||||
//! #[hdl]
|
||||
//! let v: UInt<8> = m.input();
|
||||
//! #[hdl]
|
||||
//! let w: Array<UInt<8>, 4> = wire();
|
||||
//! connect(
|
||||
//! let w: Array<[UInt<8>; 4]> = m.wire();
|
||||
//! m.connect(
|
||||
//! w,
|
||||
//! #[hdl]
|
||||
//! [4_hdl_u8, v, 3_hdl_u8, (v + 7_hdl_u8).cast_to_static()] // you can make an array like this
|
||||
//! [4_hdl_u8, v, 3_hdl_u8, (v + 7_hdl_u8).cast()] // you can make an array like this
|
||||
//! );
|
||||
//! connect(
|
||||
//! m.connect(
|
||||
//! w,
|
||||
//! #[hdl]
|
||||
//! [(v + 1_hdl_u8).cast_to_static(); 4] // or you can make an array repeat like this
|
||||
//! [(v + 1_hdl_u8).cast(); 4] // or you can make an array repeat like this
|
||||
//! );
|
||||
//! # }
|
||||
//! ```
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
//! # `#[hdl] if` Statements
|
||||
//!
|
||||
//! `#[hdl] if` statements behave similarly to Rust `if` statements, except they end up as muxes
|
||||
//! and stuff in the final hardware instead of being run when the fayalite module is being created.
|
||||
//!
|
||||
//! The condition of an `#[hdl] if` statement must have type [`Expr<Bool>`][Bool].
|
||||
//! The condition of an `#[hdl] if` statement must have type [`UInt<1>`] or [`DynUInt`] with
|
||||
//! `width() == 1` or be an [expression][Expr] of one of those types.
|
||||
//!
|
||||
//! `#[hdl] if` statements' bodies must evaluate to type `()` for now.
|
||||
//!
|
||||
|
@ -15,4 +14,7 @@
|
|||
//! [match]: super::hdl_match_statements
|
||||
|
||||
#[allow(unused)]
|
||||
use crate::int::Bool;
|
||||
use crate::{
|
||||
expr::Expr,
|
||||
int::{DynUInt, UInt},
|
||||
};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
//! ## `#[hdl] let` statements
|
||||
|
||||
pub mod inputs_outputs;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
//! ### Inputs/Outputs
|
||||
//!
|
||||
//! Inputs/Outputs create a Rust variable with type [`Expr<T>`] where `T` is the type of the input/output.
|
||||
|
@ -8,14 +6,14 @@
|
|||
//! so you should read it.
|
||||
//!
|
||||
//! ```
|
||||
//! # use fayalite::prelude::*;
|
||||
//! # use fayalite::{hdl_module, int::UInt, expr::Expr, array::Array};
|
||||
//! # #[hdl_module]
|
||||
//! # fn module() {
|
||||
//! #[hdl]
|
||||
//! let my_input: UInt<10> = m.input();
|
||||
//! let _: Expr<UInt<10>> = my_input; // my_input has type Expr<UInt<10>>
|
||||
//! #[hdl]
|
||||
//! let my_output: Array<UInt<10>, 3> = m.output();
|
||||
//! let my_output: Array<[UInt<10>; 3]> = m.output();
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
|
|
|
@ -1,20 +1,18 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
//! ### Module Instances
|
||||
//!
|
||||
//! module instances are kinda like the hardware equivalent of calling a function,
|
||||
//! you can create them like so:
|
||||
//!
|
||||
//! ```
|
||||
//! # use fayalite::prelude::*;
|
||||
//! # use fayalite::{hdl_module, int::UInt, array::Array};
|
||||
//! # #[hdl_module]
|
||||
//! # fn module() {
|
||||
//! #[hdl]
|
||||
//! let my_instance = instance(some_module());
|
||||
//! let my_instance = m.instance(some_module());
|
||||
//! // now you can use `my_instance`'s inputs/outputs like so:
|
||||
//! #[hdl]
|
||||
//! let v: UInt<3> = m.input();
|
||||
//! connect(my_instance.a, v);
|
||||
//! m.connect(my_instance.a, v);
|
||||
//! #[hdl_module]
|
||||
//! fn some_module() {
|
||||
//! #[hdl]
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
//! # Memories
|
||||
//!
|
||||
//! Memories are optimized for storing large amounts of data.
|
||||
|
@ -9,12 +7,12 @@
|
|||
//!
|
||||
//! There are several different ways to create a memory:
|
||||
//!
|
||||
//! ## using [`memory()`]
|
||||
//! ## using [`ModuleBuilder::memory()`]
|
||||
//!
|
||||
//! This way you have to set the [`depth`][`MemBuilder::depth`] separately.
|
||||
//!
|
||||
//! ```
|
||||
//! # use fayalite::prelude::*;
|
||||
//! # use fayalite::{hdl_module, int::UInt, clock::ClockDomain};
|
||||
//! # #[hdl_module]
|
||||
//! # fn module() {
|
||||
//! // first, we need some IO
|
||||
|
@ -27,45 +25,45 @@
|
|||
//!
|
||||
//! // now create the memory
|
||||
//! #[hdl]
|
||||
//! let mut my_memory = memory();
|
||||
//! let mut my_memory = m.memory();
|
||||
//! my_memory.depth(256); // the memory has 256 elements
|
||||
//!
|
||||
//! let read_port = my_memory.new_read_port();
|
||||
//!
|
||||
//! // connect up the read port
|
||||
//! connect_any(read_port.addr, read_addr);
|
||||
//! connect(read_port.en, true);
|
||||
//! connect(read_port.clk, cd.clk);
|
||||
//! connect(read_data, read_port.data);
|
||||
//! m.connect_any(read_port.addr, read_addr);
|
||||
//! m.connect(read_port.en, 1_hdl_u1);
|
||||
//! m.connect(read_port.clk, cd.clk);
|
||||
//! m.connect(read_data, read_port.data);
|
||||
//!
|
||||
//! // we need more IO for the write port
|
||||
//! #[hdl]
|
||||
//! let write_addr: UInt<8> = m.input();
|
||||
//! #[hdl]
|
||||
//! let do_write: Bool = m.input();
|
||||
//! let do_write: UInt<1> = m.input();
|
||||
//! #[hdl]
|
||||
//! let write_data: UInt<8> = m.input();
|
||||
//!
|
||||
//! let write_port = my_memory.new_write_port();
|
||||
//!
|
||||
//! connect_any(write_port.addr, write_addr);
|
||||
//! connect(write_port.en, do_write);
|
||||
//! connect(write_port.clk, cd.clk);
|
||||
//! connect(write_port.data, write_port.data);
|
||||
//! connect(write_port.mask, true);
|
||||
//! m.connect_any(write_port.addr, write_addr);
|
||||
//! m.connect(write_port.en, do_write);
|
||||
//! m.connect(write_port.clk, cd.clk);
|
||||
//! m.connect(write_port.data, write_port.data);
|
||||
//! m.connect(write_port.mask, 1_hdl_u1);
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ## using [`memory_array()`]
|
||||
//! ## using [`ModuleBuilder::memory_array()`]
|
||||
//!
|
||||
//! this allows you to specify the memory's underlying array type directly.
|
||||
//!
|
||||
//! ```
|
||||
//! # use fayalite::prelude::*;
|
||||
//! # use fayalite::{hdl_module, int::UInt, memory::MemBuilder};
|
||||
//! # #[hdl_module]
|
||||
//! # fn module() {
|
||||
//! #[hdl]
|
||||
//! let mut my_memory: MemBuilder<UInt<8>, ConstUsize<256>> = memory_array();
|
||||
//! let mut my_memory: MemBuilder<[UInt<8>; 256]> = m.memory_array();
|
||||
//!
|
||||
//! let read_port = my_memory.new_read_port();
|
||||
//! // ...
|
||||
|
@ -74,22 +72,25 @@
|
|||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ## using [`memory_with_init()`]
|
||||
//! ## using [`ModuleBuilder::memory_with_init()`]
|
||||
//!
|
||||
//! This allows you to deduce the memory's array type from the data used to initialize the memory.
|
||||
//!
|
||||
//! ```
|
||||
//! # use fayalite::prelude::*;
|
||||
//! # use fayalite::{hdl_module, int::UInt};
|
||||
//! # #[hdl_module]
|
||||
//! # fn module() {
|
||||
//! # #[hdl]
|
||||
//! # let read_addr: UInt<2> = m.input();
|
||||
//! #[hdl]
|
||||
//! let mut my_memory = memory_with_init([0x12_hdl_u8, 0x34_hdl_u8, 0x56_hdl_u8, 0x78_hdl_u8]);
|
||||
//! let mut my_memory = m.memory_with_init(
|
||||
//! #[hdl]
|
||||
//! [0x12_hdl_u8, 0x34_hdl_u8, 0x56_hdl_u8, 0x78_hdl_u8],
|
||||
//! );
|
||||
//!
|
||||
//! let read_port = my_memory.new_read_port();
|
||||
//! // note that `read_addr` is `UInt<2>` since the memory only has 4 elements
|
||||
//! connect_any(read_port.addr, read_addr);
|
||||
//! m.connect_any(read_port.addr, read_addr);
|
||||
//! // ...
|
||||
//! let write_port = my_memory.new_write_port();
|
||||
//! // ...
|
||||
|
@ -97,4 +98,4 @@
|
|||
//! ```
|
||||
|
||||
#[allow(unused)]
|
||||
use crate::prelude::*;
|
||||
use crate::{memory::MemBuilder, module::ModuleBuilder};
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
//! ### Registers
|
||||
//!
|
||||
//! Registers are memory devices that will change their state only on a clock
|
||||
|
@ -9,23 +7,20 @@
|
|||
//!
|
||||
//! Registers follow [connection semantics], which are unlike assignments in software, so you should read it.
|
||||
//!
|
||||
//! By convention, register names end in `_reg` -- this helps you tell which values are written
|
||||
//! immediately or on the next clock edge when connecting to them.
|
||||
//!
|
||||
//! ```
|
||||
//! # use fayalite::prelude::*;
|
||||
//! # use fayalite::{hdl_module, int::UInt, array::Array, clock::ClockDomain};
|
||||
//! # #[hdl_module]
|
||||
//! # fn module() {
|
||||
//! # #[hdl]
|
||||
//! # let v: Bool = m.input();
|
||||
//! # let v: UInt<1> = m.input();
|
||||
//! #[hdl]
|
||||
//! let cd: ClockDomain = m.input();
|
||||
//! #[hdl]
|
||||
//! let my_reg: UInt<8> = reg_builder().clock_domain(cd).reset(8_hdl_u8);
|
||||
//! let my_register: UInt<8> = m.reg_builder().clock_domain(cd).reset(8_hdl_u8);
|
||||
//! #[hdl]
|
||||
//! if v {
|
||||
//! // my_reg is only changed when both `v` is set and `cd`'s clock edge occurs.
|
||||
//! connect(my_reg, 0x45_hdl_u8);
|
||||
//! // my_register is only changed when both `v` is set and `cd`'s clock edge occurs.
|
||||
//! m.connect(my_register, 0x45_hdl_u8);
|
||||
//! }
|
||||
//! # }
|
||||
//! ```
|
||||
|
|
|
@ -1,29 +1,27 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
//! ### Wires
|
||||
//!
|
||||
//! Wires are kinda like variables, but unlike registers,
|
||||
//! they have no memory (they're combinatorial).
|
||||
//! You must [connect] to all wires, so they have a defined value.
|
||||
//! You must [connect][`ModuleBuilder::connect`] to all wires, so they have a defined value.
|
||||
//!
|
||||
//! Wires create a Rust variable with type [`Expr<T>`] where `T` is the type of the wire.
|
||||
//!
|
||||
//! Wires follow [connection semantics], which are unlike assignments in software, so you should read it.
|
||||
//!
|
||||
//! ```
|
||||
//! # use fayalite::prelude::*;
|
||||
//! # use fayalite::{hdl_module, int::UInt, array::Array, clock::ClockDomain};
|
||||
//! # #[hdl_module]
|
||||
//! # fn module() {
|
||||
//! # #[hdl]
|
||||
//! # let v: Bool = m.input();
|
||||
//! # let v: UInt<1> = m.input();
|
||||
//! #[hdl]
|
||||
//! let my_wire: UInt<8> = wire();
|
||||
//! let my_wire: UInt<8> = m.wire();
|
||||
//! #[hdl]
|
||||
//! if v {
|
||||
//! connect(my_wire, 0x45_hdl_u8);
|
||||
//! m.connect(my_wire, 0x45_hdl_u8);
|
||||
//! } else {
|
||||
//! // wires must be connected to under all conditions
|
||||
//! connect(my_wire, 0x23_hdl_u8);
|
||||
//! m.connect(my_wire, 0x23_hdl_u8);
|
||||
//! }
|
||||
//! # }
|
||||
//! ```
|
||||
|
@ -31,4 +29,4 @@
|
|||
//! [connection semantics]: crate::_docs::semantics::connection_semantics
|
||||
|
||||
#[allow(unused)]
|
||||
use crate::prelude::*;
|
||||
use crate::{expr::Expr, module::ModuleBuilder};
|
||||
|
|
|
@ -1,10 +1,9 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
//! # `_hdl`-suffixed literals
|
||||
//!
|
||||
//! You can have integer literals with an arbitrary number of bits like so:
|
||||
//!
|
||||
//! `_hdl`-suffixed literals have type [`Expr<UInt<N>>`] or [`Expr<SInt<N>>`].
|
||||
//! `_hdl`-suffixed literals have type [`Expr<UInt<N>>`] or [`Expr<SInt<N>>`]
|
||||
//! ... which are basically just [`UInt<N>`] or [`SInt<N>`] converted to an expression.
|
||||
//!
|
||||
//! ```
|
||||
//! # #[fayalite::hdl_module]
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
//! # `#[hdl] match` Statements
|
||||
//!
|
||||
//! `#[hdl] match` statements behave similarly to Rust `match` statements, except they end up as muxes
|
||||
|
@ -8,4 +6,4 @@
|
|||
//! `#[hdl] match` statements' bodies must evaluate to type `()` for now.
|
||||
//!
|
||||
//! `#[hdl] match` statements can only match one level of struct/enum pattern for now,
|
||||
//! e.g. you can match with the pattern `HdlSome(v)`, but not `HdlSome(HdlSome(_))`.
|
||||
//! e.g. you can match with the pattern `Some(v)`, but not `Some(Some(_))`.
|
||||
|
|
|
@ -1,27 +1,28 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
//! # `#[hdl]` Struct/Variant Expressions
|
||||
//!
|
||||
//! Note: Structs are also known as [Bundles] when used in Fayalite, the Bundle name comes from [FIRRTL].
|
||||
//!
|
||||
//! [Bundles]: crate::bundle::BundleType
|
||||
//! [Bundles]: crate::bundle::BundleValue
|
||||
//! [FIRRTL]: https://github.com/chipsalliance/firrtl-spec
|
||||
//!
|
||||
//! `#[hdl]` can be used on Struct Expressions to construct a value of that
|
||||
//! struct's type. They can also be used on tuples.
|
||||
//! `#[hdl]` can be used on Struct/Variant Expressions to construct a value of that
|
||||
//! struct/variant's type. They can also be used on tuples.
|
||||
//!
|
||||
//! ```
|
||||
//! # use fayalite::prelude::*;
|
||||
//! #[hdl]
|
||||
//! # use fayalite::{hdl_module, int::UInt, array::Array, ty::Value};
|
||||
//! #[derive(Value, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
//! #[hdl(static)]
|
||||
//! pub struct MyStruct {
|
||||
//! pub a: UInt<8>,
|
||||
//! pub b: UInt<16>,
|
||||
//! }
|
||||
//!
|
||||
//! #[hdl]
|
||||
//! #[derive(Value, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
//! pub enum MyEnum {
|
||||
//! A,
|
||||
//! B(UInt<32>),
|
||||
//! B {
|
||||
//! v: UInt<32>,
|
||||
//! },
|
||||
//! }
|
||||
//!
|
||||
//! # #[hdl_module]
|
||||
|
@ -29,8 +30,8 @@
|
|||
//! #[hdl]
|
||||
//! let v: UInt<8> = m.input();
|
||||
//! #[hdl]
|
||||
//! let my_struct: MyStruct = wire();
|
||||
//! connect(
|
||||
//! let my_struct: MyStruct = m.wire();
|
||||
//! m.connect(
|
||||
//! my_struct,
|
||||
//! #[hdl]
|
||||
//! MyStruct {
|
||||
|
@ -39,14 +40,15 @@
|
|||
//! },
|
||||
//! );
|
||||
//! #[hdl]
|
||||
//! let my_enum: MyEnum = wire();
|
||||
//! connect(
|
||||
//! let my_enum: MyEnum = m.wire();
|
||||
//! m.connect(
|
||||
//! my_enum,
|
||||
//! MyEnum.B(12345678_hdl_u32),
|
||||
//! #[hdl]
|
||||
//! MyEnum::B { v: 12345678_hdl_u32 },
|
||||
//! );
|
||||
//! #[hdl]
|
||||
//! let some_tuple: (UInt<4>, UInt<12>) = wire();
|
||||
//! connect(
|
||||
//! let some_tuple: (UInt<4>, UInt<12>) = m.wire();
|
||||
//! m.connect(
|
||||
//! some_tuple,
|
||||
//! #[hdl]
|
||||
//! (12_hdl_u4, 3421_hdl_u12),
|
||||
|
@ -55,4 +57,4 @@
|
|||
//! ```
|
||||
|
||||
#[allow(unused)]
|
||||
use crate::prelude::*;
|
||||
use crate::array::Array;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
//! # Normal Modules
|
||||
//!
|
||||
//! These use the [`NormalModule`][`crate::module::NormalModule`] tag type.
|
||||
//!
|
||||
//! See also: [Extern Modules][`super::extern_module`]
|
||||
//! See also: [Module Bodies][`super::module_bodies`]
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
//! # Fayalite Semantics
|
||||
//!
|
||||
//! Fayalite's semantics are based on [FIRRTL]. Due to their significance, some of the semantics are also documented here.
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
//! # Connection Semantics
|
||||
//!
|
||||
//! Fayalite's connection semantics are unlike assignments in software, so be careful!
|
||||
|
@ -22,60 +20,62 @@
|
|||
//! Connection Semantics Example:
|
||||
//!
|
||||
//! ```
|
||||
//! # use fayalite::prelude::*;
|
||||
//! # use fayalite::{hdl_module, int::UInt};
|
||||
//! # #[hdl_module]
|
||||
//! # fn module() {
|
||||
//! #[hdl]
|
||||
//! let a: UInt<8> = wire();
|
||||
//! let a: UInt<8> = m.wire();
|
||||
//! #[hdl]
|
||||
//! let b: UInt<8> = m.output();
|
||||
//!
|
||||
//! // doesn't actually affect anything, since `a` is completely overwritten later
|
||||
//! connect(a, 5_hdl_u8);
|
||||
//! m.connect(a, 5_hdl_u8);
|
||||
//!
|
||||
//! // here `a` has value `7` since the last connection assigns
|
||||
//! // `7` to `a`, so `b` has value `7` too.
|
||||
//! connect(b, a);
|
||||
//! m.connect(b, a);
|
||||
//!
|
||||
//! // this is the last `connect` to `a`, so this `connect` determines `a`'s value
|
||||
//! connect(a, 7_hdl_u8);
|
||||
//! m.connect(a, 7_hdl_u8);
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! # Conditional Connection Semantics
|
||||
//!
|
||||
//! ```
|
||||
//! # use fayalite::prelude::*;
|
||||
//! # use fayalite::{hdl_module, int::UInt};
|
||||
//! # #[hdl_module]
|
||||
//! # fn module() {
|
||||
//! #[hdl]
|
||||
//! let cond: Bool = m.input();
|
||||
//! let cond: UInt<1> = m.input();
|
||||
//! #[hdl]
|
||||
//! let a: UInt<8> = wire();
|
||||
//! let a: UInt<8> = m.wire();
|
||||
//! #[hdl]
|
||||
//! let b: UInt<8> = m.output();
|
||||
//!
|
||||
//! // this is the last `connect` to `a` when `cond` is `0`
|
||||
//! connect(a, 5_hdl_u8);
|
||||
//! m.connect(a, 5_hdl_u8);
|
||||
//!
|
||||
//! // here `a` has value `7` if `cond` is `1` since the last connection assigns
|
||||
//! // `7` to `a`, so `b` has value `7` too, otherwise `a` (and therefore `b`)
|
||||
//! // have value `5` since then the connection assigning `7` is in a
|
||||
//! // conditional block where the condition doesn't hold.
|
||||
//! connect(b, a);
|
||||
//! m.connect(b, a);
|
||||
//!
|
||||
//! #[hdl]
|
||||
//! if cond {
|
||||
//! // this is the last `connect` to `a` when `cond` is `1`
|
||||
//! connect(a, 7_hdl_u8);
|
||||
//! m.connect(a, 7_hdl_u8);
|
||||
//! }
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! [conditional block]: self#conditional-connection-semantics
|
||||
//! [`connect()`]: ModuleBuilder::connect
|
||||
//! [`connect_any()`]: ModuleBuilder::connect_any
|
||||
//! [wire]: crate::_docs::modules::module_bodies::hdl_let_statements::wires
|
||||
//! [if]: crate::_docs::modules::module_bodies::hdl_if_statements
|
||||
//! [FIRRTL]: https://github.com/chipsalliance/firrtl-spec
|
||||
|
||||
#[allow(unused)]
|
||||
use crate::prelude::*;
|
||||
use crate::module::ModuleBuilder;
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
expr::target::Target,
|
||||
expr::Target,
|
||||
intern::{Intern, Interned},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
iter::FusedIterator,
|
||||
ops::Deref,
|
||||
};
|
||||
|
||||
|
@ -119,87 +118,11 @@ pub struct CustomFirrtlAnnotation {
|
|||
pub additional_fields: CustomFirrtlAnnotationFields,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
|
||||
pub struct DontTouchAnnotation;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
|
||||
pub struct SVAttributeAnnotation {
|
||||
pub text: Interned<str>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
|
||||
pub struct BlackBoxInlineAnnotation {
|
||||
pub path: Interned<str>,
|
||||
pub text: Interned<str>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
|
||||
pub struct BlackBoxPathAnnotation {
|
||||
pub path: Interned<str>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Serialize, Deserialize)]
|
||||
pub struct DocStringAnnotation {
|
||||
pub text: Interned<str>,
|
||||
}
|
||||
|
||||
macro_rules! make_annotation_enum {
|
||||
(
|
||||
$(#[$meta:meta])*
|
||||
$vis:vis enum $Annotation:ident {
|
||||
$($Variant:ident($T:ident),)*
|
||||
}
|
||||
) => {
|
||||
$(#[$meta])*
|
||||
$vis enum $Annotation {
|
||||
$($Variant($T),)*
|
||||
}
|
||||
|
||||
$(impl IntoAnnotations for $T {
|
||||
type IntoAnnotations = [$Annotation; 1];
|
||||
|
||||
fn into_annotations(self) -> Self::IntoAnnotations {
|
||||
[$Annotation::$Variant(self)]
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoAnnotations for &'_ $T {
|
||||
type IntoAnnotations = [$Annotation; 1];
|
||||
|
||||
fn into_annotations(self) -> Self::IntoAnnotations {
|
||||
[$Annotation::$Variant(*self)]
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoAnnotations for &'_ mut $T {
|
||||
type IntoAnnotations = [$Annotation; 1];
|
||||
|
||||
fn into_annotations(self) -> Self::IntoAnnotations {
|
||||
[$Annotation::$Variant(*self)]
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoAnnotations for Box<$T> {
|
||||
type IntoAnnotations = [$Annotation; 1];
|
||||
|
||||
fn into_annotations(self) -> Self::IntoAnnotations {
|
||||
[$Annotation::$Variant(*self)]
|
||||
}
|
||||
})*
|
||||
};
|
||||
}
|
||||
|
||||
make_annotation_enum! {
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum Annotation {
|
||||
DontTouch(DontTouchAnnotation),
|
||||
SVAttribute(SVAttributeAnnotation),
|
||||
BlackBoxInline(BlackBoxInlineAnnotation),
|
||||
BlackBoxPath(BlackBoxPathAnnotation),
|
||||
DocString(DocStringAnnotation),
|
||||
CustomFirrtl(CustomFirrtlAnnotation),
|
||||
}
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum Annotation {
|
||||
DontTouch,
|
||||
CustomFirrtl(CustomFirrtlAnnotation),
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
|
@ -264,70 +187,10 @@ impl IntoAnnotations for &'_ mut Annotation {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct IterIntoAnnotations<T: Iterator<Item: IntoAnnotations>> {
|
||||
outer: T,
|
||||
inner: Option<<<T::Item as IntoAnnotations>::IntoAnnotations as IntoIterator>::IntoIter>,
|
||||
}
|
||||
|
||||
impl<T: Iterator<Item: IntoAnnotations>> Iterator for IterIntoAnnotations<T> {
|
||||
type Item = Annotation;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
loop {
|
||||
if let Some(inner) = &mut self.inner {
|
||||
let Some(retval) = inner.next() else {
|
||||
self.inner = None;
|
||||
continue;
|
||||
};
|
||||
return Some(retval);
|
||||
} else {
|
||||
self.inner = Some(self.outer.next()?.into_annotations().into_iter());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
if let (0, Some(0)) = self.outer.size_hint() {
|
||||
self.inner
|
||||
.as_ref()
|
||||
.map(|v| v.size_hint())
|
||||
.unwrap_or((0, Some(0)))
|
||||
} else {
|
||||
(
|
||||
self.inner.as_ref().map(|v| v.size_hint().0).unwrap_or(0),
|
||||
None,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn fold<B, F>(self, init: B, f: F) -> B
|
||||
where
|
||||
Self: Sized,
|
||||
F: FnMut(B, Self::Item) -> B,
|
||||
{
|
||||
self.inner
|
||||
.into_iter()
|
||||
.chain(self.outer.map(|v| v.into_annotations().into_iter()))
|
||||
.flatten()
|
||||
.fold(init, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<
|
||||
T: FusedIterator<
|
||||
Item: IntoAnnotations<IntoAnnotations: IntoIterator<IntoIter: FusedIterator>>,
|
||||
>,
|
||||
> FusedIterator for IterIntoAnnotations<T>
|
||||
{
|
||||
}
|
||||
|
||||
impl<T: IntoIterator<Item: IntoAnnotations>> IntoAnnotations for T {
|
||||
type IntoAnnotations = IterIntoAnnotations<T::IntoIter>;
|
||||
impl<T: IntoIterator<Item = Annotation>> IntoAnnotations for T {
|
||||
type IntoAnnotations = Self;
|
||||
|
||||
fn into_annotations(self) -> Self::IntoAnnotations {
|
||||
IterIntoAnnotations {
|
||||
outer: self.into_iter(),
|
||||
inner: None,
|
||||
}
|
||||
self
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,220 +1,671 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
use crate::{
|
||||
expr::{ops::ArrayIndex, Expr, ToExpr},
|
||||
int::{DynSize, KnownSize, Size, SizeType, DYN_SIZE},
|
||||
intern::{Intern, Interned, LazyInterned},
|
||||
module::transform::visit::{Fold, Folder, Visit, Visitor},
|
||||
bundle::{BundleType, BundleValue},
|
||||
expr::{
|
||||
ops::{ArrayIndex, ArrayLiteral, ExprIndex},
|
||||
Expr, ToExpr,
|
||||
},
|
||||
intern::{Intern, Interned, InternedCompare, Memoize},
|
||||
module::{
|
||||
transform::visit::{Fold, Folder, Visit, Visitor},
|
||||
ModuleBuilder, NormalModule,
|
||||
},
|
||||
source_location::SourceLocation,
|
||||
ty::{
|
||||
CanonicalType, MatchVariantWithoutScope, StaticType, Type, TypeProperties, TypeWithDeref,
|
||||
CanonicalType, CanonicalTypeKind, CanonicalValue, Connect, DynCanonicalType,
|
||||
DynCanonicalValue, DynType, DynValueTrait, MatchVariantWithoutScope, StaticType,
|
||||
StaticValue, Type, TypeEnum, Value, ValueEnum,
|
||||
},
|
||||
util::ConstUsize,
|
||||
util::{ConstBool, GenericConstBool, MakeMutSlice},
|
||||
};
|
||||
use bitvec::{slice::BitSlice, vec::BitVec};
|
||||
use std::{
|
||||
any::Any,
|
||||
borrow::{Borrow, BorrowMut},
|
||||
fmt,
|
||||
hash::Hash,
|
||||
marker::PhantomData,
|
||||
ops::IndexMut,
|
||||
sync::Arc,
|
||||
};
|
||||
use std::ops::Index;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ArrayType<T: Type = CanonicalType, Len: Size = DynSize> {
|
||||
element: LazyInterned<T>,
|
||||
len: Len::SizeType,
|
||||
type_properties: TypeProperties,
|
||||
mod sealed {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
|
||||
impl<T: Type, Len: Size> std::fmt::Debug for ArrayType<T, Len> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "Array<{:?}, {}>", self.element, self.len())
|
||||
pub trait ValueArrayOrSlice:
|
||||
sealed::Sealed
|
||||
+ BorrowMut<[<Self as ValueArrayOrSlice>::Element]>
|
||||
+ AsRef<[<Self as ValueArrayOrSlice>::Element]>
|
||||
+ AsMut<[<Self as ValueArrayOrSlice>::Element]>
|
||||
+ Hash
|
||||
+ fmt::Debug
|
||||
+ Eq
|
||||
+ Send
|
||||
+ Sync
|
||||
+ 'static
|
||||
+ IndexMut<usize, Output = <Self as ValueArrayOrSlice>::Element>
|
||||
+ ToOwned
|
||||
+ InternedCompare
|
||||
{
|
||||
type Element: Value<Type = <Self as ValueArrayOrSlice>::ElementType>;
|
||||
type ElementType: Type<Value = <Self as ValueArrayOrSlice>::Element>;
|
||||
type LenType: 'static + Copy + Ord + fmt::Debug + Hash + Send + Sync;
|
||||
type Match: 'static
|
||||
+ Clone
|
||||
+ Eq
|
||||
+ fmt::Debug
|
||||
+ Hash
|
||||
+ Send
|
||||
+ Sync
|
||||
+ BorrowMut<[Expr<Self::Element>]>;
|
||||
type MaskVA: ValueArrayOrSlice<
|
||||
Element = <Self::ElementType as Type>::MaskValue,
|
||||
ElementType = <Self::ElementType as Type>::MaskType,
|
||||
LenType = Self::LenType,
|
||||
MaskVA = Self::MaskVA,
|
||||
> + ?Sized;
|
||||
type IsStaticLen: GenericConstBool;
|
||||
const FIXED_LEN_TYPE: Option<Self::LenType>;
|
||||
fn make_match(array: Expr<Array<Self>>) -> Self::Match;
|
||||
fn len_from_len_type(v: Self::LenType) -> usize;
|
||||
#[allow(clippy::result_unit_err)]
|
||||
fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()>;
|
||||
fn len_type(&self) -> Self::LenType;
|
||||
fn len(&self) -> usize;
|
||||
fn is_empty(&self) -> bool;
|
||||
fn iter(&self) -> std::slice::Iter<Self::Element> {
|
||||
Borrow::<[_]>::borrow(self).iter()
|
||||
}
|
||||
fn clone_to_arc(&self) -> Arc<Self>;
|
||||
fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self;
|
||||
fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]>;
|
||||
}
|
||||
|
||||
impl<T> sealed::Sealed for [T] {}
|
||||
|
||||
impl<V: Value> ValueArrayOrSlice for [V]
|
||||
where
|
||||
V::Type: Type<Value = V>,
|
||||
{
|
||||
type Element = V;
|
||||
type ElementType = V::Type;
|
||||
type LenType = usize;
|
||||
type Match = Box<[Expr<V>]>;
|
||||
type MaskVA = [<Self::ElementType as Type>::MaskValue];
|
||||
type IsStaticLen = ConstBool<false>;
|
||||
const FIXED_LEN_TYPE: Option<Self::LenType> = None;
|
||||
|
||||
fn make_match(array: Expr<Array<Self>>) -> Self::Match {
|
||||
(0..array.canonical_type().len())
|
||||
.map(|index| ArrayIndex::<V::Type>::new_unchecked(array.canonical(), index).to_expr())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn len_from_len_type(v: Self::LenType) -> usize {
|
||||
v
|
||||
}
|
||||
|
||||
fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()> {
|
||||
Ok(v)
|
||||
}
|
||||
|
||||
fn len_type(&self) -> Self::LenType {
|
||||
self.len()
|
||||
}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
<[_]>::len(self)
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
<[_]>::is_empty(self)
|
||||
}
|
||||
|
||||
fn clone_to_arc(&self) -> Arc<Self> {
|
||||
Arc::from(self)
|
||||
}
|
||||
|
||||
fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self {
|
||||
MakeMutSlice::make_mut_slice(v)
|
||||
}
|
||||
|
||||
fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub type Array<T = CanonicalType, const LEN: usize = DYN_SIZE> = ArrayType<T, ConstUsize<LEN>>;
|
||||
impl<T, const N: usize> sealed::Sealed for [T; N] {}
|
||||
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const Array: ArrayWithoutGenerics = ArrayWithoutGenerics;
|
||||
#[allow(non_upper_case_globals)]
|
||||
pub const ArrayType: ArrayWithoutGenerics = ArrayWithoutGenerics;
|
||||
impl<V: Value, const N: usize> ValueArrayOrSlice for [V; N]
|
||||
where
|
||||
V::Type: Type<Value = V>,
|
||||
{
|
||||
type Element = V;
|
||||
type ElementType = V::Type;
|
||||
type LenType = ();
|
||||
type Match = [Expr<V>; N];
|
||||
type MaskVA = [<Self::ElementType as Type>::MaskValue; N];
|
||||
type IsStaticLen = ConstBool<true>;
|
||||
const FIXED_LEN_TYPE: Option<Self::LenType> = Some(());
|
||||
|
||||
impl<T: Type, Len: Size> ArrayType<T, Len> {
|
||||
const fn make_type_properties(element: TypeProperties, len: usize) -> TypeProperties {
|
||||
let TypeProperties {
|
||||
is_passive,
|
||||
is_storable,
|
||||
is_castable_from_bits,
|
||||
bit_width,
|
||||
} = element;
|
||||
let Some(bit_width) = bit_width.checked_mul(len) else {
|
||||
panic!("array too big");
|
||||
};
|
||||
TypeProperties {
|
||||
is_passive,
|
||||
is_storable,
|
||||
is_castable_from_bits,
|
||||
bit_width,
|
||||
fn make_match(array: Expr<Array<Self>>) -> Self::Match {
|
||||
std::array::from_fn(|index| {
|
||||
ArrayIndex::<V::Type>::new_unchecked(array.canonical(), index).to_expr()
|
||||
})
|
||||
}
|
||||
|
||||
fn len_from_len_type(_v: Self::LenType) -> usize {
|
||||
N
|
||||
}
|
||||
|
||||
fn try_len_type_from_len(v: usize) -> Result<Self::LenType, ()> {
|
||||
if v == N {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
pub fn new(element: T, len: Len::SizeType) -> Self {
|
||||
let type_properties =
|
||||
Self::make_type_properties(element.canonical().type_properties(), Len::as_usize(len));
|
||||
|
||||
fn len_type(&self) -> Self::LenType {}
|
||||
|
||||
fn len(&self) -> usize {
|
||||
N
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
N == 0
|
||||
}
|
||||
|
||||
fn clone_to_arc(&self) -> Arc<Self> {
|
||||
Arc::new(self.clone())
|
||||
}
|
||||
|
||||
fn arc_make_mut(v: &mut Arc<Self>) -> &mut Self {
|
||||
Arc::make_mut(v)
|
||||
}
|
||||
|
||||
fn arc_to_arc_slice(self: Arc<Self>) -> Arc<[Self::Element]> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct ArrayType<VA: ValueArrayOrSlice + ?Sized> {
|
||||
element: VA::ElementType,
|
||||
len: VA::LenType,
|
||||
bit_width: usize,
|
||||
}
|
||||
|
||||
pub trait ArrayTypeTrait:
|
||||
Type<
|
||||
CanonicalType = ArrayType<[DynCanonicalValue]>,
|
||||
Value = Array<<Self as ArrayTypeTrait>::ValueArrayOrSlice>,
|
||||
CanonicalValue = Array<[DynCanonicalValue]>,
|
||||
MaskType = ArrayType<
|
||||
<<Self as ArrayTypeTrait>::ValueArrayOrSlice as ValueArrayOrSlice>::MaskVA,
|
||||
>,
|
||||
> + From<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>>
|
||||
+ Into<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>>
|
||||
+ BorrowMut<ArrayType<<Self as ArrayTypeTrait>::ValueArrayOrSlice>>
|
||||
+ sealed::Sealed
|
||||
+ Connect<Self>
|
||||
{
|
||||
type ValueArrayOrSlice: ValueArrayOrSlice<Element = Self::Element, ElementType = Self::ElementType>
|
||||
+ ?Sized;
|
||||
type Element: Value<Type = Self::ElementType>;
|
||||
type ElementType: Type<Value = Self::Element>;
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> sealed::Sealed for ArrayType<VA> {}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> ArrayTypeTrait for ArrayType<VA> {
|
||||
type ValueArrayOrSlice = VA;
|
||||
type Element = VA::Element;
|
||||
type ElementType = VA::ElementType;
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Clone for ArrayType<VA> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
element: LazyInterned::Interned(element.intern_sized()),
|
||||
len,
|
||||
type_properties,
|
||||
element: self.element.clone(),
|
||||
len: self.len,
|
||||
bit_width: self.bit_width,
|
||||
}
|
||||
}
|
||||
pub fn element(&self) -> T {
|
||||
*self.element
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Copy for ArrayType<VA> where VA::ElementType: Copy {}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> ArrayType<VA> {
|
||||
pub fn element(&self) -> &VA::ElementType {
|
||||
&self.element
|
||||
}
|
||||
pub fn len(self) -> usize {
|
||||
Len::as_usize(self.len)
|
||||
pub fn len(&self) -> usize {
|
||||
VA::len_from_len_type(self.len)
|
||||
}
|
||||
pub fn is_empty(self) -> bool {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
pub fn type_properties(self) -> TypeProperties {
|
||||
self.type_properties
|
||||
pub fn bit_width(&self) -> usize {
|
||||
self.bit_width
|
||||
}
|
||||
pub fn as_dyn_array(self) -> Array {
|
||||
Array::new_dyn(self.element().canonical(), self.len())
|
||||
pub fn into_slice_type(self) -> ArrayType<[VA::Element]> {
|
||||
ArrayType {
|
||||
len: self.len(),
|
||||
element: self.element,
|
||||
bit_width: self.bit_width,
|
||||
}
|
||||
}
|
||||
pub fn can_connect<T2: Type, Len2: Size>(self, rhs: ArrayType<T2, Len2>) -> bool {
|
||||
self.len() == rhs.len()
|
||||
&& self
|
||||
.element()
|
||||
.canonical()
|
||||
.can_connect(rhs.element().canonical())
|
||||
#[track_caller]
|
||||
pub fn new_with_len(element: VA::ElementType, len: usize) -> Self {
|
||||
Self::new_with_len_type(
|
||||
element,
|
||||
VA::try_len_type_from_len(len).expect("length should match"),
|
||||
)
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn new_with_len_type(element: VA::ElementType, len: VA::LenType) -> Self {
|
||||
let Some(bit_width) = VA::len_from_len_type(len).checked_mul(element.bit_width()) else {
|
||||
panic!("array is too big: bit-width overflowed");
|
||||
};
|
||||
ArrayType {
|
||||
element,
|
||||
len,
|
||||
bit_width,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type, Len: KnownSize + Size<SizeType = Len>> ArrayType<T, Len> {
|
||||
pub fn new_static(element: T) -> Self {
|
||||
Self::new(element, Len::SizeType::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: StaticType, Len: KnownSize> StaticType for ArrayType<T, Len> {
|
||||
const TYPE: Self = Self {
|
||||
element: LazyInterned::new_lazy(&|| T::TYPE.intern_sized()),
|
||||
len: Len::SIZE,
|
||||
type_properties: Self::TYPE_PROPERTIES,
|
||||
};
|
||||
const MASK_TYPE: Self::MaskType = ArrayType::<T::MaskType, Len> {
|
||||
element: LazyInterned::new_lazy(&|| T::MASK_TYPE.intern_sized()),
|
||||
len: Len::SIZE,
|
||||
type_properties: Self::MASK_TYPE_PROPERTIES,
|
||||
};
|
||||
const TYPE_PROPERTIES: TypeProperties =
|
||||
Self::make_type_properties(T::TYPE_PROPERTIES, Len::VALUE);
|
||||
const MASK_TYPE_PROPERTIES: TypeProperties =
|
||||
Self::make_type_properties(T::MASK_TYPE_PROPERTIES, Len::VALUE);
|
||||
}
|
||||
|
||||
impl<T: Type> Array<T> {
|
||||
pub fn new_dyn(element: T, len: usize) -> Self {
|
||||
Self::new(element, len)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type + Fold<State>, Len: Size, State: Folder + ?Sized> Fold<State> for ArrayType<T, Len> {
|
||||
impl<VA: ValueArrayOrSlice + ?Sized, State: ?Sized + Folder> Fold<State> for ArrayType<VA>
|
||||
where
|
||||
VA::ElementType: Fold<State>,
|
||||
{
|
||||
fn fold(self, state: &mut State) -> Result<Self, State::Error> {
|
||||
state.fold_array_type(self)
|
||||
}
|
||||
|
||||
fn default_fold(self, state: &mut State) -> Result<Self, <State as Folder>::Error> {
|
||||
Ok(ArrayType::new(self.element().fold(state)?, self.len))
|
||||
fn default_fold(self, state: &mut State) -> Result<Self, State::Error> {
|
||||
Ok(Self::new_with_len_type(self.element.fold(state)?, self.len))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type + Visit<State>, Len: Size, State: Visitor + ?Sized> Visit<State>
|
||||
for ArrayType<T, Len>
|
||||
impl<VA: ValueArrayOrSlice + ?Sized, State: ?Sized + Visitor> Visit<State> for ArrayType<VA>
|
||||
where
|
||||
VA::ElementType: Visit<State>,
|
||||
{
|
||||
fn visit(&self, state: &mut State) -> Result<(), State::Error> {
|
||||
state.visit_array_type(self)
|
||||
}
|
||||
|
||||
fn default_visit(&self, state: &mut State) -> Result<(), State::Error> {
|
||||
self.element().visit(state)
|
||||
self.element.visit(state)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type, Len: Size> Type for ArrayType<T, Len> {
|
||||
type BaseType = Array;
|
||||
type MaskType = ArrayType<T::MaskType, Len>;
|
||||
type MatchVariant = Len::ArrayMatch<T>;
|
||||
impl<V: Value<Type: Type<Value = V>>, const N: usize> ArrayType<[V; N]> {
|
||||
pub fn new_array(element: V::Type) -> Self {
|
||||
ArrayType::new_with_len_type(element, ())
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: StaticValue, const N: usize> StaticType for ArrayType<[V; N]> {
|
||||
fn static_type() -> Self {
|
||||
Self::new_array(StaticType::static_type())
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: Value<Type: Type<Value = V>>> ArrayType<[V]> {
|
||||
pub fn new_slice(element: V::Type, len: usize) -> Self {
|
||||
ArrayType::new_with_len_type(element, len)
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Type for ArrayType<VA> {
|
||||
type CanonicalType = ArrayType<[DynCanonicalValue]>;
|
||||
type Value = Array<VA>;
|
||||
type CanonicalValue = Array<[DynCanonicalValue]>;
|
||||
type MaskType = ArrayType<VA::MaskVA>;
|
||||
type MaskValue = Array<VA::MaskVA>;
|
||||
type MatchVariant = VA::Match;
|
||||
type MatchActiveScope = ();
|
||||
type MatchVariantAndInactiveScope = MatchVariantWithoutScope<Len::ArrayMatch<T>>;
|
||||
type MatchVariantAndInactiveScope = MatchVariantWithoutScope<VA::Match>;
|
||||
type MatchVariantsIter = std::iter::Once<Self::MatchVariantAndInactiveScope>;
|
||||
|
||||
fn match_variants(
|
||||
this: Expr<Self>,
|
||||
fn match_variants<IO: BundleValue>(
|
||||
this: Expr<Self::Value>,
|
||||
module_builder: &mut ModuleBuilder<IO, NormalModule>,
|
||||
source_location: SourceLocation,
|
||||
) -> Self::MatchVariantsIter {
|
||||
let base = Expr::as_dyn_array(this);
|
||||
let base_ty = Expr::ty(base);
|
||||
) -> Self::MatchVariantsIter
|
||||
where
|
||||
IO::Type: BundleType<Value = IO>,
|
||||
{
|
||||
let _ = module_builder;
|
||||
let _ = source_location;
|
||||
let retval = Vec::from_iter((0..base_ty.len()).map(|i| ArrayIndex::new(base, i).to_expr()));
|
||||
std::iter::once(MatchVariantWithoutScope(
|
||||
Len::ArrayMatch::<T>::try_from(retval)
|
||||
.ok()
|
||||
.expect("unreachable"),
|
||||
))
|
||||
std::iter::once(MatchVariantWithoutScope(VA::make_match(this)))
|
||||
}
|
||||
|
||||
fn mask_type(&self) -> Self::MaskType {
|
||||
ArrayType::new(self.element().mask_type(), self.len)
|
||||
#[derive(Clone, Hash, Eq, PartialEq)]
|
||||
struct ArrayMaskTypeMemoize<T: ArrayTypeTrait>(PhantomData<T>);
|
||||
impl<T: ArrayTypeTrait> Copy for ArrayMaskTypeMemoize<T> {}
|
||||
impl<T: ArrayTypeTrait> Memoize for ArrayMaskTypeMemoize<T> {
|
||||
type Input = ArrayType<T::ValueArrayOrSlice>;
|
||||
type InputOwned = ArrayType<T::ValueArrayOrSlice>;
|
||||
type Output = <ArrayType<T::ValueArrayOrSlice> as Type>::MaskType;
|
||||
|
||||
fn inner(self, input: &Self::Input) -> Self::Output {
|
||||
ArrayType::new_with_len_type(input.element.mask_type(), input.len)
|
||||
}
|
||||
}
|
||||
ArrayMaskTypeMemoize::<Self>(PhantomData).get(self)
|
||||
}
|
||||
|
||||
fn canonical(&self) -> CanonicalType {
|
||||
CanonicalType::Array(Array::new_dyn(self.element().canonical(), self.len()))
|
||||
fn canonical(&self) -> Self::CanonicalType {
|
||||
ArrayType {
|
||||
element: self.element.canonical_dyn(),
|
||||
len: self.len(),
|
||||
bit_width: self.bit_width,
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn from_canonical(canonical_type: CanonicalType) -> Self {
|
||||
let CanonicalType::Array(array) = canonical_type else {
|
||||
panic!("expected array");
|
||||
};
|
||||
Self::new(
|
||||
T::from_canonical(array.element()),
|
||||
Len::from_usize(array.len()),
|
||||
)
|
||||
}
|
||||
fn source_location() -> SourceLocation {
|
||||
fn source_location(&self) -> SourceLocation {
|
||||
SourceLocation::builtin()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type, Len: Size> TypeWithDeref for ArrayType<T, Len> {
|
||||
fn expr_deref(this: &Expr<Self>) -> &Self::MatchVariant {
|
||||
let base = Expr::as_dyn_array(*this);
|
||||
let base_ty = Expr::ty(base);
|
||||
let retval = Vec::from_iter((0..base_ty.len()).map(|i| ArrayIndex::new(base, i).to_expr()));
|
||||
Interned::into_inner(Intern::intern_sized(
|
||||
Len::ArrayMatch::<T>::try_from(retval)
|
||||
.ok()
|
||||
.expect("unreachable"),
|
||||
))
|
||||
fn type_enum(&self) -> TypeEnum {
|
||||
TypeEnum::ArrayType(self.canonical())
|
||||
}
|
||||
|
||||
fn from_canonical_type(t: Self::CanonicalType) -> Self {
|
||||
Self {
|
||||
element: VA::ElementType::from_dyn_canonical_type(t.element),
|
||||
len: VA::try_len_type_from_len(t.len).expect("length should match"),
|
||||
bit_width: t.bit_width,
|
||||
}
|
||||
}
|
||||
|
||||
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
|
||||
Some(<dyn Any>::downcast_ref::<ArrayType<[DynCanonicalValue]>>(
|
||||
this,
|
||||
)?)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
|
||||
pub struct ArrayWithoutGenerics;
|
||||
impl<Lhs: ValueArrayOrSlice + ?Sized, Rhs: ValueArrayOrSlice + ?Sized> Connect<ArrayType<Rhs>>
|
||||
for ArrayType<Lhs>
|
||||
{
|
||||
}
|
||||
|
||||
impl<T: Type> Index<T> for ArrayWithoutGenerics {
|
||||
type Output = ArrayWithoutLen<T>;
|
||||
impl CanonicalType for ArrayType<[DynCanonicalValue]> {
|
||||
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::ArrayType;
|
||||
}
|
||||
|
||||
fn index(&self, element: T) -> &Self::Output {
|
||||
Interned::into_inner(Intern::intern_sized(ArrayWithoutLen { element }))
|
||||
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Array<VA: ValueArrayOrSlice + ?Sized> {
|
||||
element_ty: VA::ElementType,
|
||||
value: Arc<VA>,
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Clone for Array<VA> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
element_ty: self.element_ty.clone(),
|
||||
value: self.value.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct ArrayWithoutLen<T: Type> {
|
||||
element: T,
|
||||
}
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> ToExpr for Array<VA> {
|
||||
type Type = ArrayType<VA>;
|
||||
|
||||
impl<T: Type, L: SizeType> Index<L> for ArrayWithoutLen<T> {
|
||||
type Output = ArrayType<T, L::Size>;
|
||||
fn ty(&self) -> Self::Type {
|
||||
ArrayType::new_with_len_type(self.element_ty.clone(), self.value.len_type())
|
||||
}
|
||||
|
||||
fn index(&self, len: L) -> &Self::Output {
|
||||
Interned::into_inner(Intern::intern_sized(ArrayType::new(self.element, len)))
|
||||
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
|
||||
Expr::from_value(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Value for Array<VA> {
|
||||
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
|
||||
Array {
|
||||
element_ty: self.element_ty.canonical_dyn(),
|
||||
value: AsRef::<[_]>::as_ref(&*self.value)
|
||||
.iter()
|
||||
.map(|v| v.to_canonical_dyn())
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
|
||||
#[derive(Hash, Eq, PartialEq)]
|
||||
struct ArrayToBitsMemoize<VA: ValueArrayOrSlice + ?Sized>(PhantomData<VA>);
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Clone for ArrayToBitsMemoize<VA> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Copy for ArrayToBitsMemoize<VA> {}
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Memoize for ArrayToBitsMemoize<VA> {
|
||||
type Input = Array<VA>;
|
||||
type InputOwned = Array<VA>;
|
||||
type Output = Interned<BitSlice>;
|
||||
|
||||
fn inner(self, input: &Self::Input) -> Self::Output {
|
||||
let mut bits = BitVec::with_capacity(input.ty().bit_width());
|
||||
for element in AsRef::<[_]>::as_ref(&*input.value).iter() {
|
||||
bits.extend_from_bitslice(&element.to_bits());
|
||||
}
|
||||
Intern::intern_owned(bits)
|
||||
}
|
||||
}
|
||||
ArrayToBitsMemoize::<VA>(PhantomData).get(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl CanonicalValue for Array<[DynCanonicalValue]> {
|
||||
fn value_enum_impl(this: &Self) -> ValueEnum {
|
||||
ValueEnum::Array(this.clone())
|
||||
}
|
||||
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
|
||||
Value::to_bits_impl(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Array<VA> {
|
||||
pub fn element_ty(&self) -> &VA::ElementType {
|
||||
&self.element_ty
|
||||
}
|
||||
pub fn len(&self) -> usize {
|
||||
VA::len_from_len_type(self.value.len_type())
|
||||
}
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
pub fn value(&self) -> &Arc<VA> {
|
||||
&self.value
|
||||
}
|
||||
pub fn set_element(&mut self, index: usize, element: VA::Element) {
|
||||
assert_eq!(self.element_ty, element.ty());
|
||||
VA::arc_make_mut(&mut self.value)[index] = element;
|
||||
}
|
||||
pub fn new(element_ty: VA::ElementType, value: Arc<VA>) -> Self {
|
||||
for element in value.iter() {
|
||||
assert_eq!(element_ty, element.ty());
|
||||
}
|
||||
Self { element_ty, value }
|
||||
}
|
||||
pub fn into_slice(self) -> Array<[VA::Element]> {
|
||||
Array {
|
||||
element_ty: self.element_ty,
|
||||
value: self.value.arc_to_arc_slice(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice + ?Sized, T: Into<Arc<VA>>> From<T> for Array<VA>
|
||||
where
|
||||
VA::Element: StaticValue,
|
||||
{
|
||||
fn from(value: T) -> Self {
|
||||
Self::new(StaticType::static_type(), value.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: ToExpr<Type = T>, T: StaticType<MaskType: StaticType>> ToExpr for [E] {
|
||||
type Type = ArrayType<[T::Value]>;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
ArrayType::new_with_len_type(StaticType::static_type(), self.len())
|
||||
}
|
||||
|
||||
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
|
||||
let elements = Intern::intern_owned(Vec::from_iter(
|
||||
self.iter().map(|v| v.to_expr().to_canonical_dyn()),
|
||||
));
|
||||
ArrayLiteral::new_unchecked(elements, self.ty()).to_expr()
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: ToExpr<Type = T>, T: StaticType<MaskType: StaticType>> ToExpr for Vec<E> {
|
||||
type Type = ArrayType<[T::Value]>;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
<[E]>::ty(self)
|
||||
}
|
||||
|
||||
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
|
||||
<[E]>::to_expr(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: ToExpr<Type = T>, T: StaticType<MaskType: StaticType>, const N: usize> ToExpr for [E; N] {
|
||||
type Type = ArrayType<[T::Value; N]>;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
ArrayType::new_with_len_type(StaticType::static_type(), ())
|
||||
}
|
||||
|
||||
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
|
||||
let elements = Intern::intern_owned(Vec::from_iter(
|
||||
self.iter().map(|v| v.to_expr().to_canonical_dyn()),
|
||||
));
|
||||
ArrayLiteral::new_unchecked(elements, self.ty()).to_expr()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ArrayIntoIter<VA: ValueArrayOrSlice> {
|
||||
array: Arc<VA>,
|
||||
indexes: std::ops::Range<usize>,
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> Iterator for ArrayIntoIter<VA> {
|
||||
type Item = VA::Element;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
Some(self.array[self.indexes.next()?].clone())
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.indexes.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> std::iter::FusedIterator for ArrayIntoIter<VA> {}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> ExactSizeIterator for ArrayIntoIter<VA> {}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> DoubleEndedIterator for ArrayIntoIter<VA> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
Some(self.array[self.indexes.next_back()?].clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> Array<VA> {
|
||||
pub fn iter(&self) -> std::slice::Iter<'_, VA::Element> {
|
||||
self.value.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, VA: ValueArrayOrSlice> IntoIterator for &'a Array<VA> {
|
||||
type Item = &'a VA::Element;
|
||||
type IntoIter = std::slice::Iter<'a, VA::Element>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.value.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> IntoIterator for Array<VA> {
|
||||
type Item = VA::Element;
|
||||
type IntoIter = ArrayIntoIter<VA>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
ArrayIntoIter {
|
||||
indexes: 0..self.len(),
|
||||
array: self.value,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ArrayExprIter<VA: ValueArrayOrSlice> {
|
||||
array: Expr<Array<VA>>,
|
||||
indexes: std::ops::Range<usize>,
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> Iterator for ArrayExprIter<VA> {
|
||||
type Item = Expr<VA::Element>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
Some(ExprIndex::expr_index(self.array, self.indexes.next()?))
|
||||
}
|
||||
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.indexes.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> std::iter::FusedIterator for ArrayExprIter<VA> {}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> ExactSizeIterator for ArrayExprIter<VA> {}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> DoubleEndedIterator for ArrayExprIter<VA> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
Some(ExprIndex::expr_index(self.array, self.indexes.next_back()?))
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> IntoIterator for Expr<Array<VA>> {
|
||||
type Item = Expr<VA::Element>;
|
||||
type IntoIter = ArrayExprIter<VA>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> IntoIterator for &'_ Expr<Array<VA>> {
|
||||
type Item = Expr<VA::Element>;
|
||||
type IntoIter = ArrayExprIter<VA>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl<VA: ValueArrayOrSlice> Expr<Array<VA>> {
|
||||
pub fn len(self) -> usize {
|
||||
self.canonical_type().len()
|
||||
}
|
||||
pub fn is_empty(self) -> bool {
|
||||
self.canonical_type().is_empty()
|
||||
}
|
||||
pub fn iter(self) -> ArrayExprIter<VA> {
|
||||
ArrayExprIter {
|
||||
indexes: 0..self.len(),
|
||||
array: self,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,27 +1,15 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
bundle::{Bundle, BundleType},
|
||||
firrtl::{self, ExportOptions},
|
||||
bundle::{BundleType, BundleValue, DynBundle},
|
||||
firrtl,
|
||||
intern::Interned,
|
||||
module::Module,
|
||||
util::{job_server::AcquiredJob, streaming_read_utf8::streaming_read_utf8},
|
||||
};
|
||||
use clap::{
|
||||
builder::{OsStringValueParser, TypedValueParser},
|
||||
Parser, Subcommand, ValueEnum, ValueHint,
|
||||
Args, Parser, Subcommand, ValueEnum, ValueHint,
|
||||
};
|
||||
use eyre::{eyre, Report};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
error,
|
||||
ffi::OsString,
|
||||
fmt::{self, Write},
|
||||
fs, io, mem,
|
||||
path::{Path, PathBuf},
|
||||
process,
|
||||
};
|
||||
use tempfile::TempDir;
|
||||
use std::{error, ffi::OsString, fmt, io, path::PathBuf, process};
|
||||
|
||||
pub type Result<T = (), E = CliError> = std::result::Result<T, E>;
|
||||
|
||||
|
@ -49,157 +37,80 @@ impl From<io::Error> for CliError {
|
|||
|
||||
pub trait RunPhase<Arg> {
|
||||
type Output;
|
||||
fn run(&self, arg: Arg) -> Result<Self::Output> {
|
||||
self.run_with_job(arg, &mut AcquiredJob::acquire())
|
||||
}
|
||||
fn run_with_job(&self, arg: Arg, acquired_job: &mut AcquiredJob) -> Result<Self::Output>;
|
||||
fn run(&self, arg: Arg) -> Result<Self::Output>;
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
#[derive(Args, Debug)]
|
||||
#[non_exhaustive]
|
||||
pub struct BaseArgs {
|
||||
/// the directory to put the generated main output file and associated files in
|
||||
#[arg(short, long, value_hint = ValueHint::DirPath, required = true)]
|
||||
pub output: Option<PathBuf>,
|
||||
#[arg(short, long, value_hint = ValueHint::DirPath)]
|
||||
pub output: PathBuf,
|
||||
/// the stem of the generated main output file, e.g. to get foo.v, pass --file-stem=foo
|
||||
#[arg(long)]
|
||||
pub file_stem: Option<String>,
|
||||
#[arg(long, env = "FAYALITE_KEEP_TEMP_DIR")]
|
||||
pub keep_temp_dir: bool,
|
||||
#[arg(skip = false)]
|
||||
pub redirect_output_for_rust_test: bool,
|
||||
}
|
||||
|
||||
impl BaseArgs {
|
||||
fn make_firrtl_file_backend(&self) -> Result<(firrtl::FileBackend, Option<TempDir>)> {
|
||||
let (dir_path, temp_dir) = match &self.output {
|
||||
Some(output) => (output.clone(), None),
|
||||
None => {
|
||||
let temp_dir = TempDir::new()?;
|
||||
if self.keep_temp_dir {
|
||||
let temp_dir = temp_dir.into_path();
|
||||
println!("created temporary directory: {}", temp_dir.display());
|
||||
(temp_dir, None)
|
||||
} else {
|
||||
(temp_dir.path().to_path_buf(), Some(temp_dir))
|
||||
}
|
||||
}
|
||||
};
|
||||
Ok((
|
||||
firrtl::FileBackend {
|
||||
dir_path,
|
||||
top_fir_file_stem: self.file_stem.clone(),
|
||||
circuit_name: None,
|
||||
},
|
||||
temp_dir,
|
||||
))
|
||||
}
|
||||
/// handles possibly redirecting the command's output for Rust tests
|
||||
pub fn run_external_command(
|
||||
&self,
|
||||
_acquired_job: &mut AcquiredJob,
|
||||
mut command: process::Command,
|
||||
mut captured_output: Option<&mut String>,
|
||||
) -> io::Result<process::ExitStatus> {
|
||||
if self.redirect_output_for_rust_test || captured_output.is_some() {
|
||||
let (reader, writer) = os_pipe::pipe()?;
|
||||
let mut reader = io::BufReader::new(reader);
|
||||
command.stderr(writer.try_clone()?);
|
||||
command.stdout(writer); // must not leave writer around after spawning child
|
||||
command.stdin(process::Stdio::null());
|
||||
let mut child = command.spawn()?;
|
||||
drop(command); // close writers
|
||||
Ok(loop {
|
||||
let status = child.try_wait()?;
|
||||
streaming_read_utf8(&mut reader, |s| {
|
||||
if let Some(captured_output) = captured_output.as_deref_mut() {
|
||||
captured_output.push_str(s);
|
||||
}
|
||||
// use print! so output goes to Rust test output capture
|
||||
print!("{s}");
|
||||
io::Result::Ok(())
|
||||
})?;
|
||||
if let Some(status) = status {
|
||||
break status;
|
||||
}
|
||||
})
|
||||
} else {
|
||||
command.status()
|
||||
pub fn to_firrtl_file_backend(&self) -> firrtl::FileBackend {
|
||||
firrtl::FileBackend {
|
||||
dir_path: self.output.clone(),
|
||||
top_fir_file_stem: self.file_stem.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
#[derive(Args, Debug)]
|
||||
#[non_exhaustive]
|
||||
pub struct FirrtlArgs {
|
||||
#[command(flatten)]
|
||||
pub base: BaseArgs,
|
||||
#[command(flatten)]
|
||||
pub export_options: ExportOptions,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub struct FirrtlOutput {
|
||||
pub file_stem: String,
|
||||
pub top_module: String,
|
||||
pub output_dir: PathBuf,
|
||||
pub temp_dir: Option<TempDir>,
|
||||
}
|
||||
|
||||
impl FirrtlOutput {
|
||||
pub fn file_with_ext(&self, ext: &str) -> PathBuf {
|
||||
let mut retval = self.output_dir.join(&self.file_stem);
|
||||
retval.set_extension(ext);
|
||||
pub fn firrtl_file(&self, args: &FirrtlArgs) -> PathBuf {
|
||||
let mut retval = args.base.output.join(&self.file_stem);
|
||||
retval.set_extension("fir");
|
||||
retval
|
||||
}
|
||||
pub fn firrtl_file(&self) -> PathBuf {
|
||||
self.file_with_ext("fir")
|
||||
}
|
||||
}
|
||||
|
||||
impl FirrtlArgs {
|
||||
fn run_impl(
|
||||
&self,
|
||||
top_module: Module<Bundle>,
|
||||
_acquired_job: &mut AcquiredJob,
|
||||
) -> Result<FirrtlOutput> {
|
||||
let (file_backend, temp_dir) = self.base.make_firrtl_file_backend()?;
|
||||
fn run_impl(&self, top_module: Module<DynBundle>) -> Result<FirrtlOutput> {
|
||||
let firrtl::FileBackend {
|
||||
top_fir_file_stem,
|
||||
circuit_name,
|
||||
dir_path,
|
||||
} = firrtl::export(file_backend, &top_module, self.export_options)?;
|
||||
top_fir_file_stem, ..
|
||||
} = firrtl::export(self.base.to_firrtl_file_backend(), &top_module)?;
|
||||
Ok(FirrtlOutput {
|
||||
file_stem: top_fir_file_stem.expect(
|
||||
"export is known to set the file stem from the circuit name if not provided",
|
||||
),
|
||||
top_module: circuit_name.expect("export is known to set the circuit name"),
|
||||
output_dir: dir_path,
|
||||
temp_dir,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: BundleType> RunPhase<Module<T>> for FirrtlArgs {
|
||||
impl<T: BundleValue> RunPhase<Module<T>> for FirrtlArgs
|
||||
where
|
||||
T::Type: BundleType<Value = T>,
|
||||
{
|
||||
type Output = FirrtlOutput;
|
||||
fn run_with_job(
|
||||
&self,
|
||||
top_module: Module<T>,
|
||||
acquired_job: &mut AcquiredJob,
|
||||
) -> Result<Self::Output> {
|
||||
self.run_impl(top_module.canonical(), acquired_job)
|
||||
fn run(&self, top_module: Module<T>) -> Result<Self::Output> {
|
||||
self.run_impl(top_module.canonical())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: BundleType> RunPhase<Interned<Module<T>>> for FirrtlArgs {
|
||||
impl<T: BundleValue> RunPhase<Interned<Module<T>>> for FirrtlArgs
|
||||
where
|
||||
T::Type: BundleType<Value = T>,
|
||||
{
|
||||
type Output = FirrtlOutput;
|
||||
fn run_with_job(
|
||||
&self,
|
||||
top_module: Interned<Module<T>>,
|
||||
acquired_job: &mut AcquiredJob,
|
||||
) -> Result<Self::Output> {
|
||||
self.run_with_job(*top_module, acquired_job)
|
||||
fn run(&self, top_module: Interned<Module<T>>) -> Result<Self::Output> {
|
||||
self.run(*top_module)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -215,22 +126,7 @@ pub enum VerilogDialect {
|
|||
Yosys,
|
||||
}
|
||||
|
||||
impl fmt::Display for VerilogDialect {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
impl VerilogDialect {
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
VerilogDialect::Questa => "questa",
|
||||
VerilogDialect::Spyglass => "spyglass",
|
||||
VerilogDialect::Verilator => "verilator",
|
||||
VerilogDialect::Vivado => "vivado",
|
||||
VerilogDialect::Yosys => "yosys",
|
||||
}
|
||||
}
|
||||
pub fn firtool_extra_args(self) -> &'static [&'static str] {
|
||||
match self {
|
||||
VerilogDialect::Questa => &["--lowering-options=emitWireInPorts"],
|
||||
|
@ -248,7 +144,7 @@ impl VerilogDialect {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Parser, Debug, Clone)]
|
||||
#[derive(Args, Debug)]
|
||||
#[non_exhaustive]
|
||||
pub struct VerilogArgs {
|
||||
#[command(flatten)]
|
||||
|
@ -266,94 +162,39 @@ pub struct VerilogArgs {
|
|||
/// adapt the generated Verilog for a particular toolchain
|
||||
#[arg(long)]
|
||||
pub verilog_dialect: Option<VerilogDialect>,
|
||||
#[arg(long, short = 'g')]
|
||||
pub debug: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub struct VerilogOutput {
|
||||
pub firrtl: FirrtlOutput,
|
||||
pub verilog_files: Vec<PathBuf>,
|
||||
pub contents_hash: Option<blake3::Hash>,
|
||||
}
|
||||
|
||||
impl VerilogOutput {
|
||||
pub fn main_verilog_file(&self) -> PathBuf {
|
||||
self.firrtl.file_with_ext("v")
|
||||
}
|
||||
fn unadjusted_verilog_file(&self) -> PathBuf {
|
||||
self.firrtl.file_with_ext("unadjusted.v")
|
||||
pub fn verilog_file(&self, args: &VerilogArgs) -> PathBuf {
|
||||
let mut retval = args.firrtl.base.output.join(&self.firrtl.file_stem);
|
||||
retval.set_extension("v");
|
||||
retval
|
||||
}
|
||||
}
|
||||
|
||||
impl VerilogArgs {
|
||||
fn process_unadjusted_verilog_file(&self, mut output: VerilogOutput) -> Result<VerilogOutput> {
|
||||
let input = fs::read_to_string(output.unadjusted_verilog_file())?;
|
||||
let file_separator_prefix = "\n// ----- 8< ----- FILE \"";
|
||||
let file_separator_suffix = "\" ----- 8< -----\n\n";
|
||||
let mut input = &*input;
|
||||
output.contents_hash = Some(blake3::hash(input.as_bytes()));
|
||||
let main_verilog_file = output.main_verilog_file();
|
||||
let mut file_name: Option<&Path> = Some(&main_verilog_file);
|
||||
loop {
|
||||
let (chunk, next_file_name) = if let Some((chunk, rest)) =
|
||||
input.split_once(file_separator_prefix)
|
||||
{
|
||||
let Some((next_file_name, rest)) = rest.split_once(file_separator_suffix) else {
|
||||
return Err(CliError(eyre!("parsing firtool's output failed: found {file_separator_prefix:?} but no {file_separator_suffix:?}")));
|
||||
};
|
||||
input = rest;
|
||||
(chunk, Some(next_file_name.as_ref()))
|
||||
} else {
|
||||
(mem::take(&mut input), None)
|
||||
};
|
||||
let Some(file_name) = mem::replace(&mut file_name, next_file_name) else {
|
||||
break;
|
||||
};
|
||||
let file_name = output.firrtl.output_dir.join(file_name);
|
||||
fs::write(&file_name, chunk)?;
|
||||
if let Some(extension) = file_name.extension() {
|
||||
if extension == "v" || extension == "sv" {
|
||||
output.verilog_files.push(file_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(output)
|
||||
}
|
||||
fn run_impl(
|
||||
&self,
|
||||
firrtl_output: FirrtlOutput,
|
||||
acquired_job: &mut AcquiredJob,
|
||||
) -> Result<VerilogOutput> {
|
||||
let Self {
|
||||
firrtl,
|
||||
firtool,
|
||||
firtool_extra_args,
|
||||
verilog_dialect,
|
||||
debug,
|
||||
} = self;
|
||||
fn run_impl(&self, firrtl_output: FirrtlOutput) -> Result<VerilogOutput> {
|
||||
let output = VerilogOutput {
|
||||
firrtl: firrtl_output,
|
||||
verilog_files: vec![],
|
||||
contents_hash: None,
|
||||
};
|
||||
let mut cmd = process::Command::new(firtool);
|
||||
cmd.arg(output.firrtl.firrtl_file());
|
||||
let mut cmd = process::Command::new(&self.firtool);
|
||||
cmd.arg(output.firrtl.firrtl_file(&self.firrtl));
|
||||
cmd.arg("-o");
|
||||
cmd.arg(output.unadjusted_verilog_file());
|
||||
if *debug {
|
||||
cmd.arg("-g");
|
||||
cmd.arg("--preserve-values=all");
|
||||
}
|
||||
if let Some(dialect) = verilog_dialect {
|
||||
cmd.arg(output.verilog_file(self));
|
||||
if let Some(dialect) = self.verilog_dialect {
|
||||
cmd.args(dialect.firtool_extra_args());
|
||||
}
|
||||
cmd.args(firtool_extra_args);
|
||||
cmd.current_dir(&output.firrtl.output_dir);
|
||||
let status = firrtl.base.run_external_command(acquired_job, cmd, None)?;
|
||||
cmd.args(&self.firtool_extra_args);
|
||||
cmd.current_dir(&self.firrtl.base.output);
|
||||
let status = cmd.status()?;
|
||||
if status.success() {
|
||||
self.process_unadjusted_verilog_file(output)
|
||||
Ok(output)
|
||||
} else {
|
||||
Err(CliError(eyre!(
|
||||
"running {} failed: {status}",
|
||||
|
@ -368,316 +209,9 @@ where
|
|||
FirrtlArgs: RunPhase<Arg, Output = FirrtlOutput>,
|
||||
{
|
||||
type Output = VerilogOutput;
|
||||
fn run_with_job(&self, arg: Arg, acquired_job: &mut AcquiredJob) -> Result<Self::Output> {
|
||||
let firrtl_output = self.firrtl.run_with_job(arg, acquired_job)?;
|
||||
self.run_impl(firrtl_output, acquired_job)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(ValueEnum, Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
|
||||
#[non_exhaustive]
|
||||
pub enum FormalMode {
|
||||
#[default]
|
||||
BMC,
|
||||
Prove,
|
||||
Live,
|
||||
Cover,
|
||||
}
|
||||
|
||||
impl FormalMode {
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
FormalMode::BMC => "bmc",
|
||||
FormalMode::Prove => "prove",
|
||||
FormalMode::Live => "live",
|
||||
FormalMode::Cover => "cover",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for FormalMode {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(self.as_str())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct FormalAdjustArgs;
|
||||
|
||||
impl clap::FromArgMatches for FormalAdjustArgs {
|
||||
fn from_arg_matches(_matches: &clap::ArgMatches) -> Result<Self, clap::Error> {
|
||||
Ok(Self)
|
||||
}
|
||||
|
||||
fn update_from_arg_matches(&mut self, _matches: &clap::ArgMatches) -> Result<(), clap::Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl clap::Args for FormalAdjustArgs {
|
||||
fn augment_args(cmd: clap::Command) -> clap::Command {
|
||||
cmd.mut_arg("output", |arg| arg.required(false))
|
||||
.mut_arg("verilog_dialect", |arg| {
|
||||
arg.default_value(VerilogDialect::Yosys.to_string())
|
||||
.hide(true)
|
||||
})
|
||||
}
|
||||
|
||||
fn augment_args_for_update(cmd: clap::Command) -> clap::Command {
|
||||
Self::augment_args(cmd)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Parser, Clone)]
|
||||
#[non_exhaustive]
|
||||
pub struct FormalArgs {
|
||||
#[command(flatten)]
|
||||
pub verilog: VerilogArgs,
|
||||
#[arg(
|
||||
long,
|
||||
default_value = "sby",
|
||||
env = "SBY",
|
||||
value_hint = ValueHint::CommandName,
|
||||
value_parser = OsStringValueParser::new().try_map(which::which)
|
||||
)]
|
||||
pub sby: PathBuf,
|
||||
#[arg(long)]
|
||||
pub sby_extra_args: Vec<String>,
|
||||
#[arg(long, default_value_t)]
|
||||
pub mode: FormalMode,
|
||||
#[arg(long, default_value_t = Self::DEFAULT_DEPTH)]
|
||||
pub depth: u64,
|
||||
#[arg(long, default_value = "z3")]
|
||||
pub solver: String,
|
||||
#[arg(long)]
|
||||
pub smtbmc_extra_args: Vec<String>,
|
||||
#[arg(long, default_value_t = true, env = "FAYALITE_CACHE_RESULTS")]
|
||||
pub cache_results: bool,
|
||||
#[command(flatten)]
|
||||
_formal_adjust_args: FormalAdjustArgs,
|
||||
}
|
||||
|
||||
impl fmt::Debug for FormalArgs {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let Self {
|
||||
verilog,
|
||||
sby,
|
||||
sby_extra_args,
|
||||
mode,
|
||||
depth,
|
||||
solver,
|
||||
smtbmc_extra_args,
|
||||
cache_results,
|
||||
_formal_adjust_args: _,
|
||||
} = self;
|
||||
f.debug_struct("FormalArgs")
|
||||
.field("verilog", verilog)
|
||||
.field("sby", sby)
|
||||
.field("sby_extra_args", sby_extra_args)
|
||||
.field("mode", mode)
|
||||
.field("depth", depth)
|
||||
.field("solver", solver)
|
||||
.field("smtbmc_extra_args", smtbmc_extra_args)
|
||||
.field("cache_results", cache_results)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl FormalArgs {
|
||||
pub const DEFAULT_DEPTH: u64 = 20;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub struct FormalOutput {
|
||||
pub verilog: VerilogOutput,
|
||||
}
|
||||
|
||||
impl FormalOutput {
|
||||
pub fn sby_file(&self) -> PathBuf {
|
||||
self.verilog.firrtl.file_with_ext("sby")
|
||||
}
|
||||
pub fn cache_file(&self) -> PathBuf {
|
||||
self.verilog.firrtl.file_with_ext("cache.json")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[non_exhaustive]
|
||||
pub struct FormalCacheOutput {}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[non_exhaustive]
|
||||
pub enum FormalCacheVersion {
|
||||
V1,
|
||||
}
|
||||
|
||||
impl FormalCacheVersion {
|
||||
pub const CURRENT: Self = Self::V1;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
#[non_exhaustive]
|
||||
pub struct FormalCache {
|
||||
pub version: FormalCacheVersion,
|
||||
pub contents_hash: blake3::Hash,
|
||||
pub stdout_stderr: String,
|
||||
pub result: Result<FormalCacheOutput, String>,
|
||||
}
|
||||
|
||||
impl FormalCache {
|
||||
pub fn new(
|
||||
version: FormalCacheVersion,
|
||||
contents_hash: blake3::Hash,
|
||||
stdout_stderr: String,
|
||||
result: Result<FormalCacheOutput, String>,
|
||||
) -> Self {
|
||||
Self {
|
||||
version,
|
||||
contents_hash,
|
||||
stdout_stderr,
|
||||
result,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FormalArgs {
|
||||
fn sby_contents(&self, output: &FormalOutput) -> Result<String> {
|
||||
let Self {
|
||||
verilog: _,
|
||||
sby: _,
|
||||
sby_extra_args: _,
|
||||
mode,
|
||||
depth,
|
||||
smtbmc_extra_args,
|
||||
solver,
|
||||
cache_results: _,
|
||||
_formal_adjust_args: _,
|
||||
} = self;
|
||||
let smtbmc_options = smtbmc_extra_args.join(" ");
|
||||
let top_module = &output.verilog.firrtl.top_module;
|
||||
let mut retval = format!(
|
||||
"[options]\n\
|
||||
mode {mode}\n\
|
||||
depth {depth}\n\
|
||||
wait on\n\
|
||||
\n\
|
||||
[engines]\n\
|
||||
smtbmc {solver} -- -- {smtbmc_options}\n\
|
||||
\n\
|
||||
[script]\n"
|
||||
);
|
||||
for verilog_file in &output.verilog.verilog_files {
|
||||
let verilog_file = verilog_file
|
||||
.to_str()
|
||||
.ok_or_else(|| CliError(eyre!("verilog file path is not UTF-8")))?;
|
||||
if verilog_file.contains(|ch: char| {
|
||||
(ch != ' ' && ch != '\t' && ch.is_ascii_whitespace()) || ch == '"'
|
||||
}) {
|
||||
return Err(CliError(eyre!(
|
||||
"verilog file path contains characters that aren't permitted"
|
||||
)));
|
||||
}
|
||||
writeln!(retval, "read_verilog -sv -formal \"{verilog_file}\"").unwrap();
|
||||
}
|
||||
// workaround for wires disappearing -- set `keep` on all wires
|
||||
writeln!(retval, "hierarchy -top {top_module}").unwrap();
|
||||
writeln!(retval, "proc").unwrap();
|
||||
writeln!(retval, "setattr -set keep 1 w:\\*").unwrap();
|
||||
writeln!(retval, "prep").unwrap();
|
||||
Ok(retval)
|
||||
}
|
||||
fn run_impl(
|
||||
&self,
|
||||
verilog_output: VerilogOutput,
|
||||
acquired_job: &mut AcquiredJob,
|
||||
) -> Result<FormalOutput> {
|
||||
let output = FormalOutput {
|
||||
verilog: verilog_output,
|
||||
};
|
||||
let sby_file = output.sby_file();
|
||||
let sby_contents = self.sby_contents(&output)?;
|
||||
let contents_hash = output.verilog.contents_hash.map(|verilog_hash| {
|
||||
let mut hasher = blake3::Hasher::new();
|
||||
hasher.update(verilog_hash.as_bytes());
|
||||
hasher.update(sby_contents.as_bytes());
|
||||
hasher.update(&(self.sby_extra_args.len() as u64).to_le_bytes());
|
||||
for sby_extra_arg in self.sby_extra_args.iter() {
|
||||
hasher.update(&(sby_extra_arg.len() as u64).to_le_bytes());
|
||||
hasher.update(sby_extra_arg.as_bytes());
|
||||
}
|
||||
hasher.finalize()
|
||||
});
|
||||
std::fs::write(&sby_file, sby_contents)?;
|
||||
let mut cmd = process::Command::new(&self.sby);
|
||||
cmd.arg("-j1"); // sby seems not to respect job count in parallel mode
|
||||
cmd.arg("-f");
|
||||
cmd.arg(sby_file.file_name().unwrap());
|
||||
cmd.args(&self.sby_extra_args);
|
||||
cmd.current_dir(&output.verilog.firrtl.output_dir);
|
||||
let mut captured_output = String::new();
|
||||
let cache_file = output.cache_file();
|
||||
let do_cache = if let Some(contents_hash) = contents_hash.filter(|_| self.cache_results) {
|
||||
if let Some(FormalCache {
|
||||
version: FormalCacheVersion::CURRENT,
|
||||
contents_hash: cache_contents_hash,
|
||||
stdout_stderr,
|
||||
result,
|
||||
}) = fs::read(&cache_file)
|
||||
.ok()
|
||||
.and_then(|v| serde_json::from_slice(&v).ok())
|
||||
{
|
||||
if cache_contents_hash == contents_hash {
|
||||
println!("Using cached formal result:\n{stdout_stderr}");
|
||||
return match result {
|
||||
Ok(FormalCacheOutput {}) => Ok(output),
|
||||
Err(error) => Err(CliError(eyre::Report::msg(error))),
|
||||
};
|
||||
}
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
};
|
||||
let _ = fs::remove_file(&cache_file);
|
||||
let status = self.verilog.firrtl.base.run_external_command(
|
||||
acquired_job,
|
||||
cmd,
|
||||
do_cache.then_some(&mut captured_output),
|
||||
)?;
|
||||
let result = if status.success() {
|
||||
Ok(output)
|
||||
} else {
|
||||
Err(CliError(eyre!(
|
||||
"running {} failed: {status}",
|
||||
self.sby.display()
|
||||
)))
|
||||
};
|
||||
fs::write(
|
||||
cache_file,
|
||||
serde_json::to_string_pretty(&FormalCache {
|
||||
version: FormalCacheVersion::CURRENT,
|
||||
contents_hash: contents_hash.unwrap(),
|
||||
stdout_stderr: captured_output,
|
||||
result: match &result {
|
||||
Ok(FormalOutput { verilog: _ }) => Ok(FormalCacheOutput {}),
|
||||
Err(error) => Err(error.to_string()),
|
||||
},
|
||||
})
|
||||
.expect("serialization shouldn't ever fail"),
|
||||
)?;
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl<Arg> RunPhase<Arg> for FormalArgs
|
||||
where
|
||||
VerilogArgs: RunPhase<Arg, Output = VerilogOutput>,
|
||||
{
|
||||
type Output = FormalOutput;
|
||||
fn run_with_job(&self, arg: Arg, acquired_job: &mut AcquiredJob) -> Result<Self::Output> {
|
||||
let verilog_output = self.verilog.run_with_job(arg, acquired_job)?;
|
||||
self.run_impl(verilog_output, acquired_job)
|
||||
fn run(&self, arg: Arg) -> Result<Self::Output> {
|
||||
let firrtl_output = self.firrtl.run(arg)?;
|
||||
self.run_impl(firrtl_output)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -687,8 +221,6 @@ enum CliCommand {
|
|||
Firrtl(FirrtlArgs),
|
||||
/// Generate Verilog
|
||||
Verilog(VerilogArgs),
|
||||
/// Run a formal proof
|
||||
Formal(FormalArgs),
|
||||
}
|
||||
|
||||
/// a simple CLI
|
||||
|
@ -696,7 +228,7 @@ enum CliCommand {
|
|||
/// Use like:
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use fayalite::prelude::*;
|
||||
/// # use fayalite::hdl_module;
|
||||
/// # #[hdl_module]
|
||||
/// # fn my_module() {}
|
||||
/// use fayalite::cli;
|
||||
|
@ -709,7 +241,7 @@ enum CliCommand {
|
|||
/// You can also use it with a larger [`clap`]-based CLI like so:
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use fayalite::prelude::*;
|
||||
/// # use fayalite::hdl_module;
|
||||
/// # #[hdl_module]
|
||||
/// # fn my_module() {}
|
||||
/// use clap::{Subcommand, Parser};
|
||||
|
@ -766,16 +298,13 @@ where
|
|||
FirrtlArgs: RunPhase<T, Output = FirrtlOutput>,
|
||||
{
|
||||
type Output = ();
|
||||
fn run_with_job(&self, arg: T, acquired_job: &mut AcquiredJob) -> Result<Self::Output> {
|
||||
fn run(&self, arg: T) -> Result<Self::Output> {
|
||||
match &self.subcommand {
|
||||
CliCommand::Firrtl(c) => {
|
||||
c.run_with_job(arg, acquired_job)?;
|
||||
c.run(arg)?;
|
||||
}
|
||||
CliCommand::Verilog(c) => {
|
||||
c.run_with_job(arg, acquired_job)?;
|
||||
}
|
||||
CliCommand::Formal(c) => {
|
||||
c.run_with_job(arg, acquired_job)?;
|
||||
c.run(arg)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
@ -2,61 +2,111 @@
|
|||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
expr::{Expr, ToExpr},
|
||||
hdl,
|
||||
int::Bool,
|
||||
int::{UInt, UIntType},
|
||||
intern::Interned,
|
||||
reset::Reset,
|
||||
source_location::SourceLocation,
|
||||
ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties},
|
||||
ty::{
|
||||
impl_match_values_as_self, CanonicalType, CanonicalTypeKind, CanonicalValue, Connect,
|
||||
DynCanonicalType, StaticType, Type, TypeEnum, Value, ValueEnum,
|
||||
},
|
||||
util::interned_bit,
|
||||
};
|
||||
use bitvec::slice::BitSlice;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
|
||||
pub struct Clock;
|
||||
pub struct ClockType;
|
||||
|
||||
impl Type for Clock {
|
||||
type BaseType = Clock;
|
||||
type MaskType = Bool;
|
||||
impl ClockType {
|
||||
pub const fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl_match_variant_as_self!();
|
||||
impl Type for ClockType {
|
||||
type Value = Clock;
|
||||
type CanonicalType = ClockType;
|
||||
type CanonicalValue = Clock;
|
||||
type MaskType = UIntType<1>;
|
||||
type MaskValue = UInt<1>;
|
||||
|
||||
impl_match_values_as_self!();
|
||||
|
||||
fn mask_type(&self) -> Self::MaskType {
|
||||
Bool
|
||||
UIntType::new()
|
||||
}
|
||||
|
||||
fn canonical(&self) -> CanonicalType {
|
||||
CanonicalType::Clock(*self)
|
||||
fn type_enum(&self) -> TypeEnum {
|
||||
TypeEnum::Clock(*self)
|
||||
}
|
||||
|
||||
fn source_location() -> SourceLocation {
|
||||
fn from_canonical_type(t: Self::CanonicalType) -> Self {
|
||||
t
|
||||
}
|
||||
|
||||
fn canonical(&self) -> Self::CanonicalType {
|
||||
*self
|
||||
}
|
||||
|
||||
fn source_location(&self) -> SourceLocation {
|
||||
SourceLocation::builtin()
|
||||
}
|
||||
|
||||
fn from_canonical(canonical_type: CanonicalType) -> Self {
|
||||
let CanonicalType::Clock(retval) = canonical_type else {
|
||||
panic!("expected Clock");
|
||||
};
|
||||
retval
|
||||
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
|
||||
Some(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl Clock {
|
||||
pub fn type_properties(self) -> TypeProperties {
|
||||
Self::TYPE_PROPERTIES
|
||||
}
|
||||
pub fn can_connect(self, _rhs: Self) -> bool {
|
||||
true
|
||||
impl Connect<Self> for ClockType {}
|
||||
|
||||
impl CanonicalType for ClockType {
|
||||
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::Clock;
|
||||
}
|
||||
|
||||
impl StaticType for ClockType {
|
||||
fn static_type() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl StaticType for Clock {
|
||||
const TYPE: Self = Self;
|
||||
const MASK_TYPE: Self::MaskType = Bool;
|
||||
const TYPE_PROPERTIES: TypeProperties = TypeProperties {
|
||||
is_passive: true,
|
||||
is_storable: false,
|
||||
is_castable_from_bits: true,
|
||||
bit_width: 1,
|
||||
};
|
||||
const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES;
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct Clock(pub bool);
|
||||
|
||||
impl ToExpr for Clock {
|
||||
type Type = ClockType;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
ClockType
|
||||
}
|
||||
|
||||
fn to_expr(&self) -> Expr<Self> {
|
||||
Expr::from_value(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Value for Clock {
|
||||
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
|
||||
*self
|
||||
}
|
||||
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
|
||||
interned_bit(this.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl CanonicalValue for Clock {
|
||||
fn value_enum_impl(this: &Self) -> ValueEnum {
|
||||
ValueEnum::Clock(*this)
|
||||
}
|
||||
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
|
||||
interned_bit(this.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Value)]
|
||||
#[hdl(static, outline_generated)]
|
||||
pub struct ClockDomain {
|
||||
pub clk: Clock,
|
||||
pub rst: Reset,
|
||||
}
|
||||
|
||||
pub trait ToClock {
|
||||
|
@ -87,10 +137,10 @@ impl ToClock for Expr<Clock> {
|
|||
}
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
pub struct ClockDomain {
|
||||
pub clk: Clock,
|
||||
pub rst: Reset,
|
||||
impl ToClock for Clock {
|
||||
fn to_clock(&self) -> Expr<Clock> {
|
||||
self.to_expr()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToClock for bool {
|
||||
|
@ -98,3 +148,9 @@ impl ToClock for bool {
|
|||
self.to_expr().to_clock()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToClock for UInt<1> {
|
||||
fn to_clock(&self) -> Expr<Clock> {
|
||||
self.to_expr().to_clock()
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -1,434 +0,0 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
array::Array,
|
||||
bundle::{Bundle, BundleField},
|
||||
expr::Flow,
|
||||
intern::{Intern, Interned},
|
||||
memory::{DynPortType, MemPort},
|
||||
module::{Instance, ModuleIO, TargetName},
|
||||
reg::Reg,
|
||||
source_location::SourceLocation,
|
||||
ty::{CanonicalType, Type},
|
||||
wire::Wire,
|
||||
};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct TargetPathBundleField {
|
||||
pub name: Interned<str>,
|
||||
}
|
||||
|
||||
impl fmt::Display for TargetPathBundleField {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, ".{}", self.name)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct TargetPathArrayElement {
|
||||
pub index: usize,
|
||||
}
|
||||
|
||||
impl fmt::Display for TargetPathArrayElement {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "[{}]", self.index)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct TargetPathDynArrayElement {}
|
||||
|
||||
impl fmt::Display for TargetPathDynArrayElement {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "[<dyn>]")
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum TargetPathElement {
|
||||
BundleField(TargetPathBundleField),
|
||||
ArrayElement(TargetPathArrayElement),
|
||||
DynArrayElement(TargetPathDynArrayElement),
|
||||
}
|
||||
|
||||
impl From<TargetPathBundleField> for TargetPathElement {
|
||||
fn from(value: TargetPathBundleField) -> Self {
|
||||
Self::BundleField(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TargetPathArrayElement> for TargetPathElement {
|
||||
fn from(value: TargetPathArrayElement) -> Self {
|
||||
Self::ArrayElement(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TargetPathDynArrayElement> for TargetPathElement {
|
||||
fn from(value: TargetPathDynArrayElement) -> Self {
|
||||
Self::DynArrayElement(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TargetPathElement {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::BundleField(v) => v.fmt(f),
|
||||
Self::ArrayElement(v) => v.fmt(f),
|
||||
Self::DynArrayElement(v) => v.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TargetPathElement {
|
||||
pub fn canonical_ty(&self, parent: Interned<Target>) -> CanonicalType {
|
||||
match self {
|
||||
&Self::BundleField(TargetPathBundleField { name }) => {
|
||||
let parent_ty = Bundle::from_canonical(parent.canonical_ty());
|
||||
let field = parent_ty
|
||||
.field_by_name(name)
|
||||
.expect("field name is known to be a valid field of parent type");
|
||||
field.ty
|
||||
}
|
||||
&Self::ArrayElement(TargetPathArrayElement { index }) => {
|
||||
let parent_ty = Array::<CanonicalType>::from_canonical(parent.canonical_ty());
|
||||
assert!(index < parent_ty.len());
|
||||
parent_ty.element()
|
||||
}
|
||||
Self::DynArrayElement(_) => {
|
||||
let parent_ty = Array::<CanonicalType>::from_canonical(parent.canonical_ty());
|
||||
parent_ty.element()
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn flow(&self, parent: Interned<Target>) -> Flow {
|
||||
match self {
|
||||
Self::BundleField(v) => {
|
||||
let parent_ty = Bundle::from_canonical(parent.canonical_ty());
|
||||
let field = parent_ty
|
||||
.field_by_name(v.name)
|
||||
.expect("field name is known to be a valid field of parent type");
|
||||
parent.flow().flip_if(field.flipped)
|
||||
}
|
||||
Self::ArrayElement(_) => parent.flow(),
|
||||
Self::DynArrayElement(_) => parent.flow(),
|
||||
}
|
||||
}
|
||||
pub fn is_static(&self) -> bool {
|
||||
match self {
|
||||
Self::BundleField(_) | Self::ArrayElement(_) => true,
|
||||
Self::DynArrayElement(_) => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_target_base {
|
||||
(
|
||||
$(#[$enum_meta:meta])*
|
||||
$enum_vis:vis enum $TargetBase:ident {
|
||||
$(
|
||||
#[is = $is_fn:ident]
|
||||
#[to = $to_fn:ident]
|
||||
$(#[$variant_meta:meta])*
|
||||
$Variant:ident($VariantTy:ty),
|
||||
)*
|
||||
}
|
||||
) => {
|
||||
$(#[$enum_meta])*
|
||||
$enum_vis enum $TargetBase {
|
||||
$(
|
||||
$(#[$variant_meta])*
|
||||
$Variant($VariantTy),
|
||||
)*
|
||||
}
|
||||
|
||||
impl fmt::Debug for $TargetBase {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
$(Self::$Variant(v) => v.fmt(f),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(
|
||||
impl From<$VariantTy> for $TargetBase {
|
||||
fn from(value: $VariantTy) -> Self {
|
||||
Self::$Variant(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<$VariantTy> for Target {
|
||||
fn from(value: $VariantTy) -> Self {
|
||||
$TargetBase::$Variant(value).into()
|
||||
}
|
||||
}
|
||||
)*
|
||||
|
||||
impl $TargetBase {
|
||||
$(
|
||||
$enum_vis fn $is_fn(&self) -> bool {
|
||||
self.$to_fn().is_some()
|
||||
}
|
||||
$enum_vis fn $to_fn(&self) -> Option<&$VariantTy> {
|
||||
if let Self::$Variant(retval) = self {
|
||||
Some(retval)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
)*
|
||||
$enum_vis fn must_connect_to(&self) -> bool {
|
||||
match self {
|
||||
$(Self::$Variant(v) => v.must_connect_to(),)*
|
||||
}
|
||||
}
|
||||
$enum_vis fn flow(&self) -> Flow {
|
||||
match self {
|
||||
$(Self::$Variant(v) => v.flow(),)*
|
||||
}
|
||||
}
|
||||
$enum_vis fn source_location(&self) -> SourceLocation {
|
||||
match self {
|
||||
$(Self::$Variant(v) => v.source_location(),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_target_base! {
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub enum TargetBase {
|
||||
#[is = is_module_io]
|
||||
#[to = module_io]
|
||||
ModuleIO(ModuleIO<CanonicalType>),
|
||||
#[is = is_mem_port]
|
||||
#[to = mem_port]
|
||||
MemPort(MemPort<DynPortType>),
|
||||
#[is = is_reg]
|
||||
#[to = reg]
|
||||
Reg(Reg<CanonicalType>),
|
||||
#[is = is_wire]
|
||||
#[to = wire]
|
||||
Wire(Wire<CanonicalType>),
|
||||
#[is = is_instance]
|
||||
#[to = instance]
|
||||
Instance(Instance<Bundle>),
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TargetBase {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self.target_name())
|
||||
}
|
||||
}
|
||||
|
||||
impl TargetBase {
|
||||
pub fn target_name(&self) -> TargetName {
|
||||
match self {
|
||||
TargetBase::ModuleIO(v) => TargetName(v.scoped_name(), None),
|
||||
TargetBase::MemPort(v) => TargetName(v.mem_name(), Some(v.port_name())),
|
||||
TargetBase::Reg(v) => TargetName(v.scoped_name(), None),
|
||||
TargetBase::Wire(v) => TargetName(v.scoped_name(), None),
|
||||
TargetBase::Instance(v) => TargetName(v.scoped_name(), None),
|
||||
}
|
||||
}
|
||||
pub fn canonical_ty(&self) -> CanonicalType {
|
||||
match self {
|
||||
TargetBase::ModuleIO(v) => v.ty(),
|
||||
TargetBase::MemPort(v) => v.ty().canonical(),
|
||||
TargetBase::Reg(v) => v.ty(),
|
||||
TargetBase::Wire(v) => v.ty(),
|
||||
TargetBase::Instance(v) => v.ty().canonical(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct TargetChild {
|
||||
parent: Interned<Target>,
|
||||
path_element: Interned<TargetPathElement>,
|
||||
canonical_ty: CanonicalType,
|
||||
flow: Flow,
|
||||
}
|
||||
|
||||
impl fmt::Debug for TargetChild {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let Self {
|
||||
parent,
|
||||
path_element,
|
||||
canonical_ty: _,
|
||||
flow: _,
|
||||
} = self;
|
||||
parent.fmt(f)?;
|
||||
fmt::Display::fmt(path_element, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TargetChild {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let Self {
|
||||
parent,
|
||||
path_element,
|
||||
canonical_ty: _,
|
||||
flow: _,
|
||||
} = self;
|
||||
parent.fmt(f)?;
|
||||
path_element.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl TargetChild {
|
||||
pub fn new(parent: Interned<Target>, path_element: Interned<TargetPathElement>) -> Self {
|
||||
Self {
|
||||
parent,
|
||||
path_element,
|
||||
canonical_ty: path_element.canonical_ty(parent),
|
||||
flow: path_element.flow(parent),
|
||||
}
|
||||
}
|
||||
pub fn parent(self) -> Interned<Target> {
|
||||
self.parent
|
||||
}
|
||||
pub fn path_element(self) -> Interned<TargetPathElement> {
|
||||
self.path_element
|
||||
}
|
||||
pub fn canonical_ty(self) -> CanonicalType {
|
||||
self.canonical_ty
|
||||
}
|
||||
pub fn flow(self) -> Flow {
|
||||
self.flow
|
||||
}
|
||||
pub fn bundle_field(self) -> Option<BundleField> {
|
||||
if let TargetPathElement::BundleField(TargetPathBundleField { name }) = *self.path_element {
|
||||
let parent_ty = Bundle::from_canonical(self.parent.canonical_ty());
|
||||
Some(
|
||||
parent_ty
|
||||
.field_by_name(name)
|
||||
.expect("field name known to be a valid field of parent"),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub enum Target {
|
||||
Base(Interned<TargetBase>),
|
||||
Child(TargetChild),
|
||||
}
|
||||
|
||||
impl From<TargetBase> for Target {
|
||||
fn from(value: TargetBase) -> Self {
|
||||
Self::Base(Intern::intern_sized(value))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TargetChild> for Target {
|
||||
fn from(value: TargetChild) -> Self {
|
||||
Self::Child(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Interned<TargetBase>> for Target {
|
||||
fn from(value: Interned<TargetBase>) -> Self {
|
||||
Self::Base(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl Target {
|
||||
pub fn base(&self) -> Interned<TargetBase> {
|
||||
let mut target = self;
|
||||
loop {
|
||||
match target {
|
||||
Self::Base(v) => break *v,
|
||||
Self::Child(v) => target = &v.parent,
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn child(&self) -> Option<TargetChild> {
|
||||
match *self {
|
||||
Target::Base(_) => None,
|
||||
Target::Child(v) => Some(v),
|
||||
}
|
||||
}
|
||||
pub fn is_static(&self) -> bool {
|
||||
let mut target = self;
|
||||
loop {
|
||||
match target {
|
||||
Self::Base(_) => return true,
|
||||
Self::Child(v) if !v.path_element().is_static() => return false,
|
||||
Self::Child(v) => target = &v.parent,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[must_use]
|
||||
pub fn join(&self, path_element: Interned<TargetPathElement>) -> Self {
|
||||
TargetChild::new(self.intern(), path_element).into()
|
||||
}
|
||||
pub fn flow(&self) -> Flow {
|
||||
match self {
|
||||
Self::Base(v) => v.flow(),
|
||||
Self::Child(v) => v.flow(),
|
||||
}
|
||||
}
|
||||
pub fn canonical_ty(&self) -> CanonicalType {
|
||||
match self {
|
||||
Target::Base(v) => v.canonical_ty(),
|
||||
Target::Child(v) => v.canonical_ty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Target {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Base(v) => v.fmt(f),
|
||||
Self::Child(v) => v.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Target {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Base(v) => v.fmt(f),
|
||||
Self::Child(v) => v.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait GetTarget {
|
||||
fn target(&self) -> Option<Interned<Target>>;
|
||||
}
|
||||
|
||||
impl GetTarget for bool {
|
||||
fn target(&self) -> Option<Interned<Target>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + GetTarget + Send + Sync + 'static> GetTarget for Interned<T> {
|
||||
fn target(&self) -> Option<Interned<Target>> {
|
||||
T::target(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + GetTarget> GetTarget for &'_ T {
|
||||
fn target(&self) -> Option<Interned<Target>> {
|
||||
T::target(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + GetTarget> GetTarget for &'_ mut T {
|
||||
fn target(&self) -> Option<Interned<Target>> {
|
||||
T::target(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + GetTarget> GetTarget for Box<T> {
|
||||
fn target(&self) -> Option<Interned<Target>> {
|
||||
T::target(self)
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,247 +0,0 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
int::BoolOrIntType,
|
||||
intern::{Intern, Interned, Memoize},
|
||||
prelude::*,
|
||||
};
|
||||
use std::sync::OnceLock;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
pub enum FormalKind {
|
||||
Assert,
|
||||
Assume,
|
||||
Cover,
|
||||
}
|
||||
|
||||
impl FormalKind {
|
||||
pub fn as_str(self) -> &'static str {
|
||||
match self {
|
||||
Self::Assert => "assert",
|
||||
Self::Assume => "assume",
|
||||
Self::Cover => "cover",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn formal_stmt_with_enable_and_loc(
|
||||
kind: FormalKind,
|
||||
clk: Expr<Clock>,
|
||||
pred: Expr<Bool>,
|
||||
en: Expr<Bool>,
|
||||
text: &str,
|
||||
source_location: SourceLocation,
|
||||
) {
|
||||
crate::module::add_stmt_formal(crate::module::StmtFormal {
|
||||
kind,
|
||||
clk,
|
||||
pred,
|
||||
en: en & !formal_reset().cast_to_static::<Bool>(),
|
||||
text: text.intern(),
|
||||
source_location,
|
||||
});
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn formal_stmt_with_enable(
|
||||
kind: FormalKind,
|
||||
clk: Expr<Clock>,
|
||||
pred: Expr<Bool>,
|
||||
en: Expr<Bool>,
|
||||
text: &str,
|
||||
) {
|
||||
formal_stmt_with_enable_and_loc(kind, clk, pred, en, text, SourceLocation::caller());
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn formal_stmt_with_loc(
|
||||
kind: FormalKind,
|
||||
clk: Expr<Clock>,
|
||||
pred: Expr<Bool>,
|
||||
text: &str,
|
||||
source_location: SourceLocation,
|
||||
) {
|
||||
formal_stmt_with_enable_and_loc(kind, clk, pred, true.to_expr(), text, source_location);
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn formal_stmt(kind: FormalKind, clk: Expr<Clock>, pred: Expr<Bool>, text: &str) {
|
||||
formal_stmt_with_loc(kind, clk, pred, text, SourceLocation::caller());
|
||||
}
|
||||
|
||||
macro_rules! make_formal {
|
||||
($kind:ident, $formal_stmt_with_enable_and_loc:ident, $formal_stmt_with_enable:ident, $formal_stmt_with_loc:ident, $formal_stmt:ident) => {
|
||||
#[track_caller]
|
||||
pub fn $formal_stmt_with_enable_and_loc(
|
||||
clk: Expr<Clock>,
|
||||
pred: Expr<Bool>,
|
||||
en: Expr<Bool>,
|
||||
text: &str,
|
||||
source_location: SourceLocation,
|
||||
) {
|
||||
formal_stmt_with_enable_and_loc(
|
||||
FormalKind::$kind,
|
||||
clk,
|
||||
pred,
|
||||
en,
|
||||
text,
|
||||
source_location,
|
||||
);
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn $formal_stmt_with_enable(
|
||||
clk: Expr<Clock>,
|
||||
pred: Expr<Bool>,
|
||||
en: Expr<Bool>,
|
||||
text: &str,
|
||||
) {
|
||||
formal_stmt_with_enable(FormalKind::$kind, clk, pred, en, text);
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn $formal_stmt_with_loc(
|
||||
clk: Expr<Clock>,
|
||||
pred: Expr<Bool>,
|
||||
text: &str,
|
||||
source_location: SourceLocation,
|
||||
) {
|
||||
formal_stmt_with_loc(FormalKind::$kind, clk, pred, text, source_location);
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn $formal_stmt(clk: Expr<Clock>, pred: Expr<Bool>, text: &str) {
|
||||
formal_stmt(FormalKind::$kind, clk, pred, text);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
make_formal!(
|
||||
Assert,
|
||||
hdl_assert_with_enable_and_loc,
|
||||
hdl_assert_with_enable,
|
||||
hdl_assert_with_loc,
|
||||
hdl_assert
|
||||
);
|
||||
|
||||
make_formal!(
|
||||
Assume,
|
||||
hdl_assume_with_enable_and_loc,
|
||||
hdl_assume_with_enable,
|
||||
hdl_assume_with_loc,
|
||||
hdl_assume
|
||||
);
|
||||
|
||||
make_formal!(
|
||||
Cover,
|
||||
hdl_cover_with_enable_and_loc,
|
||||
hdl_cover_with_enable,
|
||||
hdl_cover_with_loc,
|
||||
hdl_cover
|
||||
);
|
||||
|
||||
pub trait MakeFormalExpr: Type {}
|
||||
|
||||
impl<T: Type> MakeFormalExpr for T {}
|
||||
|
||||
#[hdl]
|
||||
pub fn formal_global_clock() -> Expr<Clock> {
|
||||
#[hdl_module(extern)]
|
||||
fn formal_global_clock() {
|
||||
#[hdl]
|
||||
let clk: Clock = m.output();
|
||||
m.annotate_module(BlackBoxInlineAnnotation {
|
||||
path: "fayalite_formal_global_clock.v".intern(),
|
||||
text: r"module __fayalite_formal_global_clock(output clk);
|
||||
(* gclk *)
|
||||
reg clk;
|
||||
endmodule
|
||||
"
|
||||
.intern(),
|
||||
});
|
||||
m.verilog_name("__fayalite_formal_global_clock");
|
||||
}
|
||||
#[hdl]
|
||||
let formal_global_clock = instance(formal_global_clock());
|
||||
formal_global_clock.clk
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
pub fn formal_reset() -> Expr<SyncReset> {
|
||||
#[hdl_module(extern)]
|
||||
fn formal_reset() {
|
||||
#[hdl]
|
||||
let rst: SyncReset = m.output();
|
||||
m.annotate_module(BlackBoxInlineAnnotation {
|
||||
path: "fayalite_formal_reset.v".intern(),
|
||||
text: r"module __fayalite_formal_reset(output rst);
|
||||
assign rst = $initstate;
|
||||
endmodule
|
||||
"
|
||||
.intern(),
|
||||
});
|
||||
m.verilog_name("__fayalite_formal_reset");
|
||||
}
|
||||
static MOD: OnceLock<Interned<Module<formal_reset>>> = OnceLock::new();
|
||||
#[hdl]
|
||||
let formal_reset = instance(*MOD.get_or_init(formal_reset));
|
||||
formal_reset.rst
|
||||
}
|
||||
|
||||
macro_rules! make_any_const_fn {
|
||||
($ident:ident, $verilog_attribute:literal) => {
|
||||
#[hdl]
|
||||
pub fn $ident<T: BoolOrIntType>(ty: T) -> Expr<T> {
|
||||
#[hdl_module(extern)]
|
||||
pub(super) fn $ident<T: BoolOrIntType>(ty: T) {
|
||||
#[hdl]
|
||||
let out: T = m.output(ty);
|
||||
let width = ty.width();
|
||||
let verilog_bitslice = if width == 1 {
|
||||
String::new()
|
||||
} else {
|
||||
format!(" [{}:0]", width - 1)
|
||||
};
|
||||
m.annotate_module(BlackBoxInlineAnnotation {
|
||||
path: Intern::intern_owned(format!(
|
||||
"fayalite_{}_{width}.v",
|
||||
stringify!($ident),
|
||||
)),
|
||||
text: Intern::intern_owned(format!(
|
||||
r"module __fayalite_{}_{width}(output{verilog_bitslice} out);
|
||||
(* {} *)
|
||||
reg{verilog_bitslice} out;
|
||||
endmodule
|
||||
",
|
||||
stringify!($ident),
|
||||
$verilog_attribute,
|
||||
)),
|
||||
});
|
||||
m.verilog_name(format!("__fayalite_{}_{width}", stringify!($ident)));
|
||||
}
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
struct TheMemoize<T>(T);
|
||||
impl<T: BoolOrIntType> Memoize for TheMemoize<T> {
|
||||
type Input = ();
|
||||
type InputOwned = ();
|
||||
type Output = Option<Interned<Module<$ident<T>>>>;
|
||||
fn inner(self, _input: &Self::Input) -> Self::Output {
|
||||
if self.0.width() == 0 {
|
||||
None
|
||||
} else {
|
||||
Some($ident(self.0))
|
||||
}
|
||||
}
|
||||
}
|
||||
let Some(module) = TheMemoize(ty).get_owned(()) else {
|
||||
return 0_hdl_u0.cast_bits_to(ty);
|
||||
};
|
||||
#[hdl]
|
||||
let $ident = instance(module);
|
||||
$ident.out
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
make_any_const_fn!(any_const, "anyconst");
|
||||
make_any_const_fn!(any_seq, "anyseq");
|
||||
make_any_const_fn!(all_const, "allconst");
|
||||
make_any_const_fn!(all_seq, "allseq");
|
368
crates/fayalite/src/generics.rs
Normal file
368
crates/fayalite/src/generics.rs
Normal file
|
@ -0,0 +1,368 @@
|
|||
use crate::{
|
||||
intern::{Intern, Interned},
|
||||
source_location::SourceLocation,
|
||||
ty::{DynCanonicalType, Type, Value},
|
||||
util::DebugAsDisplay,
|
||||
};
|
||||
use std::{fmt, hash::Hash};
|
||||
|
||||
mod sealed {
|
||||
pub trait GenericArgsSealed {}
|
||||
pub trait ToGenericArgSealed {}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct GenericParamName {
|
||||
pub name: Interned<str>,
|
||||
pub source_location: SourceLocation,
|
||||
}
|
||||
|
||||
impl GenericParamName {
|
||||
#[track_caller]
|
||||
pub fn new(name: &str) -> Self {
|
||||
Self::new_with_loc(name, SourceLocation::caller())
|
||||
}
|
||||
pub fn new_with_loc(name: &str, source_location: SourceLocation) -> Self {
|
||||
let name = name.intern();
|
||||
Self {
|
||||
name,
|
||||
source_location,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for GenericParamName {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.name.fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct TypeParam {
|
||||
pub name: GenericParamName,
|
||||
pub default: Option<fn(earlier_args: &[GenericArg]) -> TypeArg>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for TypeParam {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("TypeParam")
|
||||
.field("name", &self.name)
|
||||
.field("default", &self.default.map(|_| DebugAsDisplay("_")))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ConstParam {
|
||||
pub name: GenericParamName,
|
||||
pub default: Option<fn(earlier_args: &[GenericArg]) -> ConstArg>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for ConstParam {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("ConstParam")
|
||||
.field("name", &self.name)
|
||||
.field("default", &self.default.map(|_| DebugAsDisplay("_")))
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum GenericParam {
|
||||
Type(TypeParam),
|
||||
Const(ConstParam),
|
||||
}
|
||||
|
||||
impl fmt::Debug for GenericParam {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Type(v) => v.fmt(f),
|
||||
Self::Const(v) => v.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct TypeArg<T: Type = Interned<dyn DynCanonicalType>> {
|
||||
pub ty: T,
|
||||
}
|
||||
|
||||
impl<T: Type> TypeArg<T> {
|
||||
pub fn canonical(&self) -> TypeArg {
|
||||
TypeArg {
|
||||
ty: self.ty.canonical_dyn(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type> sealed::ToGenericArgSealed for T {}
|
||||
|
||||
impl<T: Type> ToGenericArg for T {
|
||||
fn to_generic_arg(this: &Self) -> GenericArg {
|
||||
GenericArg::Type(TypeArg {
|
||||
ty: this.canonical_dyn(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type> sealed::ToGenericArgSealed for TypeArg<T> {}
|
||||
|
||||
impl<T: Type> ToGenericArg for TypeArg<T> {
|
||||
fn to_generic_arg(this: &Self) -> GenericArg {
|
||||
GenericArg::Type(this.canonical())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct ConstArg {
|
||||
pub value: usize,
|
||||
}
|
||||
|
||||
impl sealed::ToGenericArgSealed for usize {}
|
||||
|
||||
impl ToGenericArg for usize {
|
||||
fn to_generic_arg(this: &Self) -> GenericArg {
|
||||
GenericArg::Const(ConstArg { value: *this })
|
||||
}
|
||||
}
|
||||
|
||||
impl sealed::ToGenericArgSealed for ConstArg {}
|
||||
|
||||
impl ToGenericArg for ConstArg {
|
||||
fn to_generic_arg(this: &Self) -> GenericArg {
|
||||
GenericArg::Const(*this)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Default)]
|
||||
pub struct DefaultArg;
|
||||
|
||||
impl sealed::ToGenericArgSealed for DefaultArg {}
|
||||
|
||||
impl ToGenericArg for DefaultArg {
|
||||
fn to_generic_arg(this: &Self) -> GenericArg {
|
||||
GenericArg::Default(*this)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum GenericArg<T: Type = Interned<dyn DynCanonicalType>> {
|
||||
Type(TypeArg<T>),
|
||||
Const(ConstArg),
|
||||
Default(DefaultArg),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct NotATypeArg;
|
||||
|
||||
impl fmt::Display for NotATypeArg {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("not a type argument")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for NotATypeArg {}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct NotAConstArg;
|
||||
|
||||
impl fmt::Display for NotAConstArg {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("not a const argument")
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for NotAConstArg {}
|
||||
|
||||
impl<T: Type> GenericArg<T> {
|
||||
pub fn canonical(&self) -> GenericArg {
|
||||
match self {
|
||||
GenericArg::Type(v) => GenericArg::Type(v.canonical()),
|
||||
GenericArg::Const(v) => GenericArg::Const(*v),
|
||||
GenericArg::Default(v) => GenericArg::Default(*v),
|
||||
}
|
||||
}
|
||||
pub fn ty(self) -> Result<T, NotATypeArg> {
|
||||
match self {
|
||||
GenericArg::Type(TypeArg { ty }) => Ok(ty),
|
||||
GenericArg::Const(_) | GenericArg::Default(_) => Err(NotATypeArg),
|
||||
}
|
||||
}
|
||||
pub fn const_(self) -> Result<usize, NotAConstArg> {
|
||||
match self {
|
||||
GenericArg::Const(ConstArg { value }) => Ok(value),
|
||||
GenericArg::Type(_) | GenericArg::Default(_) => Err(NotAConstArg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type> fmt::Debug for GenericArg<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Type(v) => v.fmt(f),
|
||||
Self::Const(v) => v.fmt(f),
|
||||
Self::Default(v) => v.fmt(f),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type> sealed::ToGenericArgSealed for GenericArg<T> {}
|
||||
|
||||
impl<T: Type> ToGenericArg for GenericArg<T> {
|
||||
fn to_generic_arg(this: &Self) -> GenericArg {
|
||||
this.canonical()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ToGenericArg: sealed::ToGenericArgSealed {
|
||||
fn to_generic_arg(this: &Self) -> GenericArg;
|
||||
}
|
||||
|
||||
pub trait GenericArgs: sealed::GenericArgsSealed {
|
||||
fn generic_args(this: &Self) -> Vec<GenericArg>;
|
||||
}
|
||||
|
||||
impl<T: GenericArgs + ?Sized + Send + Sync + 'static> sealed::GenericArgsSealed for Interned<T> {}
|
||||
|
||||
impl<T: GenericArgs + ?Sized + Send + Sync + 'static> GenericArgs for Interned<T> {
|
||||
fn generic_args(this: &Self) -> Vec<GenericArg> {
|
||||
T::generic_args(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: GenericArgs + ?Sized> sealed::GenericArgsSealed for Box<T> {}
|
||||
|
||||
impl<T: GenericArgs + ?Sized> GenericArgs for Box<T> {
|
||||
fn generic_args(this: &Self) -> Vec<GenericArg> {
|
||||
T::generic_args(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: GenericArgs + ?Sized> sealed::GenericArgsSealed for &'_ T {}
|
||||
|
||||
impl<T: GenericArgs + ?Sized> GenericArgs for &'_ T {
|
||||
fn generic_args(this: &Self) -> Vec<GenericArg> {
|
||||
T::generic_args(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: GenericArgs + ?Sized> sealed::GenericArgsSealed for &'_ mut T {}
|
||||
|
||||
impl<T: GenericArgs + ?Sized> GenericArgs for &'_ mut T {
|
||||
fn generic_args(this: &Self) -> Vec<GenericArg> {
|
||||
T::generic_args(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToGenericArg> sealed::GenericArgsSealed for [T] {}
|
||||
|
||||
impl<T: ToGenericArg> GenericArgs for [T] {
|
||||
fn generic_args(this: &Self) -> Vec<GenericArg> {
|
||||
Vec::from_iter(this.iter().map(ToGenericArg::to_generic_arg))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToGenericArg> sealed::GenericArgsSealed for Vec<T> {}
|
||||
|
||||
impl<T: ToGenericArg> GenericArgs for Vec<T> {
|
||||
fn generic_args(this: &Self) -> Vec<GenericArg> {
|
||||
Vec::from_iter(this.iter().map(ToGenericArg::to_generic_arg))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ToGenericArg, const N: usize> sealed::GenericArgsSealed for [T; N] {}
|
||||
|
||||
impl<T: ToGenericArg, const N: usize> GenericArgs for [T; N] {
|
||||
fn generic_args(this: &Self) -> Vec<GenericArg> {
|
||||
Vec::from_iter(this.iter().map(ToGenericArg::to_generic_arg))
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! impl_generic_args_for_tuples {
|
||||
($($v:ident: $T:ident),*) => {
|
||||
impl<$($T: ToGenericArg,)*> sealed::GenericArgsSealed for ($($T,)*) {}
|
||||
|
||||
impl<$($T: ToGenericArg,)*> GenericArgs for ($($T,)*) {
|
||||
fn generic_args(this: &Self) -> Vec<GenericArg> {
|
||||
let ($($v,)*) = this;
|
||||
vec![$($T::to_generic_arg($v),)*]
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl_generic_args_for_tuples!();
|
||||
impl_generic_args_for_tuples!(a: A);
|
||||
impl_generic_args_for_tuples!(a: A, b: B);
|
||||
impl_generic_args_for_tuples!(a: A, b: B, c: C);
|
||||
impl_generic_args_for_tuples!(a: A, b: B, c: C, d: D);
|
||||
impl_generic_args_for_tuples!(a: A, b: B, c: C, d: D, e: E);
|
||||
impl_generic_args_for_tuples!(a: A, b: B, c: C, d: D, e: E, f: F);
|
||||
impl_generic_args_for_tuples!(a: A, b: B, c: C, d: D, e: E, f: F, g: G);
|
||||
impl_generic_args_for_tuples!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H);
|
||||
impl_generic_args_for_tuples!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I);
|
||||
impl_generic_args_for_tuples!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J);
|
||||
impl_generic_args_for_tuples!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K);
|
||||
impl_generic_args_for_tuples!(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J, k: K, l: L);
|
||||
|
||||
#[track_caller]
|
||||
pub fn canonical_generic_args(
|
||||
mut generic_args: Vec<GenericArg>,
|
||||
generic_params: &[GenericParam],
|
||||
) -> Interned<[GenericArg]> {
|
||||
assert!(generic_params.len() >= generic_args.len());
|
||||
for (i, generic_param) in generic_params.iter().enumerate() {
|
||||
match generic_args.get(i) {
|
||||
Some(GenericArg::Default(DefaultArg)) | None => {}
|
||||
Some(GenericArg::Type(_)) | Some(GenericArg::Const(_)) => continue,
|
||||
}
|
||||
let generic_arg = match generic_param {
|
||||
GenericParam::Type(TypeParam {
|
||||
name: _,
|
||||
default: Some(default),
|
||||
}) => GenericArg::Type(default(&generic_args[..i])),
|
||||
GenericParam::Type(TypeParam {
|
||||
name:
|
||||
GenericParamName {
|
||||
name,
|
||||
source_location,
|
||||
},
|
||||
default: None,
|
||||
}) => panic!("Missing type argument for {name} declared here: {source_location}"),
|
||||
GenericParam::Const(ConstParam {
|
||||
name: _,
|
||||
default: Some(default),
|
||||
}) => GenericArg::Const(default(&generic_args[..i])),
|
||||
GenericParam::Const(ConstParam {
|
||||
name:
|
||||
GenericParamName {
|
||||
name,
|
||||
source_location,
|
||||
},
|
||||
default: None,
|
||||
}) => panic!("Missing const argument for {name} declared here: {source_location}"),
|
||||
};
|
||||
if i == generic_args.len() {
|
||||
generic_args.push(generic_arg)
|
||||
} else {
|
||||
generic_args[i] = generic_arg;
|
||||
}
|
||||
}
|
||||
debug_assert_eq!(generic_params.len(), generic_args.len());
|
||||
Intern::intern_owned(generic_args)
|
||||
}
|
||||
|
||||
pub trait GenericType {
|
||||
type GenericArgs: GenericArgs;
|
||||
type Type: Type<Value = Self::Value>;
|
||||
type Value: Value<Type = Self::Type>;
|
||||
fn generic_params() -> Interned<[GenericParam]>;
|
||||
#[track_caller]
|
||||
fn canonical_generic_args(generic_args: &Self::GenericArgs) -> Interned<[GenericArg]> {
|
||||
canonical_generic_args(
|
||||
GenericArgs::generic_args(&generic_args),
|
||||
&Self::generic_params(),
|
||||
)
|
||||
}
|
||||
fn ty(generic_args: Interned<[GenericArg]>) -> Self::Type;
|
||||
}
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -12,6 +12,7 @@ extern crate self as fayalite;
|
|||
pub use std as __std;
|
||||
|
||||
#[doc(inline)]
|
||||
#[doc(alias = "hdl")]
|
||||
/// The `#[hdl_module]` attribute is applied to a Rust function so that that function creates
|
||||
/// a [`Module`][`::fayalite::module::Module`] when called.
|
||||
/// In the function body it will implicitly create a
|
||||
|
@ -20,13 +21,6 @@ pub use std as __std;
|
|||
/// See [Fayalite Modules][crate::_docs::modules]
|
||||
pub use fayalite_proc_macros::hdl_module;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use fayalite_proc_macros::hdl;
|
||||
|
||||
/// struct used as a placeholder when applying defaults
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct __;
|
||||
|
||||
#[cfg(feature = "unstable-doc")]
|
||||
pub mod _docs;
|
||||
|
||||
|
@ -38,16 +32,15 @@ pub mod clock;
|
|||
pub mod enum_;
|
||||
pub mod expr;
|
||||
pub mod firrtl;
|
||||
pub mod formal;
|
||||
pub mod generics;
|
||||
pub mod int;
|
||||
pub mod intern;
|
||||
pub mod memory;
|
||||
pub mod module;
|
||||
pub mod prelude;
|
||||
pub mod reg;
|
||||
pub mod reset;
|
||||
pub mod source_location;
|
||||
pub mod testing;
|
||||
pub mod ty;
|
||||
pub mod util;
|
||||
pub mod valueless;
|
||||
pub mod wire;
|
||||
|
|
|
@ -4,16 +4,15 @@
|
|||
#![allow(clippy::multiple_bound_locations)]
|
||||
use crate::{
|
||||
annotations::{Annotation, IntoAnnotations, TargetedAnnotation},
|
||||
array::{Array, ArrayType},
|
||||
bundle::{Bundle, BundleType},
|
||||
clock::Clock,
|
||||
expr::{ops::BundleLiteral, repeat, Expr, Flow, ToExpr, ToLiteralBits},
|
||||
hdl,
|
||||
int::{Bool, DynSize, Size, UInt, UIntType},
|
||||
array::{Array, ArrayType, ArrayTypeTrait, ValueArrayOrSlice},
|
||||
bundle::{BundleType, BundleValue, DynBundle, DynBundleType},
|
||||
clock::{Clock, ClockType},
|
||||
expr::{Expr, ExprEnum, ExprTrait, Flow, ToExpr},
|
||||
int::{DynUInt, DynUIntType, UInt, UIntType},
|
||||
intern::{Intern, Interned},
|
||||
module::ScopedNameId,
|
||||
source_location::SourceLocation,
|
||||
ty::{AsMask, CanonicalType, Type},
|
||||
ty::{AsMask, DynCanonicalType, DynCanonicalValue, DynType, Type, Value},
|
||||
util::DebugAsDisplay,
|
||||
};
|
||||
use bitvec::slice::BitSlice;
|
||||
|
@ -21,37 +20,37 @@ use std::{
|
|||
cell::RefCell,
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
marker::PhantomData,
|
||||
num::NonZeroU32,
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
#[hdl]
|
||||
pub struct ReadStruct<Element, AddrWidth: Size> {
|
||||
pub addr: UIntType<AddrWidth>,
|
||||
pub en: Bool,
|
||||
#[derive(Value, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct ReadStruct<Element> {
|
||||
pub addr: DynUInt,
|
||||
pub en: UInt<1>,
|
||||
pub clk: Clock,
|
||||
#[hdl(flip)]
|
||||
pub data: Element,
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
pub struct WriteStruct<Element, AddrWidth: Size> {
|
||||
pub addr: UIntType<AddrWidth>,
|
||||
pub en: Bool,
|
||||
#[derive(Value, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct WriteStruct<Element: Value> {
|
||||
pub addr: DynUInt,
|
||||
pub en: UInt<1>,
|
||||
pub clk: Clock,
|
||||
pub data: Element,
|
||||
pub mask: AsMask<Element>,
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
pub struct ReadWriteStruct<Element, AddrWidth: Size> {
|
||||
pub addr: UIntType<AddrWidth>,
|
||||
pub en: Bool,
|
||||
#[allow(clippy::multiple_bound_locations)]
|
||||
#[derive(Value, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct ReadWriteStruct<Element: Value> {
|
||||
pub addr: DynUInt,
|
||||
pub en: UInt<1>,
|
||||
pub clk: Clock,
|
||||
#[hdl(flip)]
|
||||
pub rdata: Element,
|
||||
pub wmode: Bool,
|
||||
pub wmode: UInt<1>,
|
||||
pub wdata: Element,
|
||||
pub wmask: AsMask<Element>,
|
||||
}
|
||||
|
@ -64,10 +63,11 @@ pub trait PortType:
|
|||
sealed::Sealed + Clone + Eq + Hash + fmt::Debug + Send + Sync + 'static
|
||||
{
|
||||
type PortKindTy: Copy + Eq + Hash + fmt::Debug + Send + Sync + 'static;
|
||||
type Port: BundleType;
|
||||
type PortType: BundleType<Value = Self::PortValue>;
|
||||
type PortValue: BundleValue<Type = Self::PortType>;
|
||||
fn port_kind(port_kind: Self::PortKindTy) -> PortKind;
|
||||
fn from_port_kind(port_kind: PortKind) -> Self::PortKindTy;
|
||||
fn port_ty(port: &MemPort<Self>) -> Self::Port;
|
||||
fn port_ty(port: &MemPort<Self>) -> Self::PortType;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
|
@ -79,7 +79,8 @@ impl sealed::Sealed for DynPortType {}
|
|||
|
||||
impl PortType for DynPortType {
|
||||
type PortKindTy = PortKind;
|
||||
type Port = Bundle;
|
||||
type PortType = DynBundleType;
|
||||
type PortValue = DynBundle;
|
||||
|
||||
fn port_kind(port_kind: Self::PortKindTy) -> PortKind {
|
||||
port_kind
|
||||
|
@ -89,38 +90,41 @@ impl PortType for DynPortType {
|
|||
port_kind
|
||||
}
|
||||
|
||||
fn port_ty(port: &MemPort<Self>) -> Self::Port {
|
||||
Bundle::new(match port.port_kind {
|
||||
PortKind::ReadOnly => {
|
||||
MemPort::<ReadStruct<CanonicalType, DynSize>>::from_canonical(*port)
|
||||
.ty()
|
||||
.fields()
|
||||
}
|
||||
PortKind::WriteOnly => {
|
||||
MemPort::<WriteStruct<CanonicalType, DynSize>>::from_canonical(*port)
|
||||
.ty()
|
||||
.fields()
|
||||
}
|
||||
fn port_ty(port: &MemPort<Self>) -> Self::PortType {
|
||||
match port.port_kind {
|
||||
PortKind::ReadOnly => MemPort::<ReadStruct<DynCanonicalValue>>::from_canonical(*port)
|
||||
.ty()
|
||||
.canonical(),
|
||||
PortKind::WriteOnly => MemPort::<WriteStruct<DynCanonicalValue>>::from_canonical(*port)
|
||||
.ty()
|
||||
.canonical(),
|
||||
PortKind::ReadWrite => {
|
||||
MemPort::<ReadWriteStruct<CanonicalType, DynSize>>::from_canonical(*port)
|
||||
MemPort::<ReadWriteStruct<DynCanonicalValue>>::from_canonical(*port)
|
||||
.ty()
|
||||
.fields()
|
||||
.canonical()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub trait PortStruct: BundleType + sealed::Sealed + PortType<PortKindTy = (), Port = Self> {
|
||||
type Element: Type;
|
||||
pub trait PortStruct:
|
||||
BundleValue
|
||||
+ sealed::Sealed
|
||||
+ PortType<PortKindTy = (), PortType = <Self as ToExpr>::Type, PortValue = Self>
|
||||
where
|
||||
Self::Type: BundleType<Value = Self>,
|
||||
{
|
||||
type Element: Value<Type = Self::ElementType>;
|
||||
type ElementType: Type<Value = Self::Element>;
|
||||
const PORT_KIND: PortKind;
|
||||
|
||||
fn addr(this: Expr<Self>) -> Expr<UInt>;
|
||||
fn en(this: Expr<Self>) -> Expr<Bool>;
|
||||
fn addr(this: Expr<Self>) -> Expr<DynUInt>;
|
||||
fn en(this: Expr<Self>) -> Expr<UInt<1>>;
|
||||
fn clk(this: Expr<Self>) -> Expr<Clock>;
|
||||
fn rdata(this: Expr<Self>) -> Option<Expr<Self::Element>>;
|
||||
fn wdata(this: Expr<Self>) -> Option<Expr<Self::Element>>;
|
||||
fn wmask(this: Expr<Self>) -> Option<Expr<AsMask<Self::Element>>>;
|
||||
fn wmode(this: Expr<Self>) -> Expr<Bool>;
|
||||
fn wmode(this: Expr<Self>) -> Expr<UInt<1>>;
|
||||
}
|
||||
|
||||
macro_rules! impl_port_struct {
|
||||
|
@ -133,11 +137,19 @@ macro_rules! impl_port_struct {
|
|||
$($body:tt)*
|
||||
}
|
||||
) => {
|
||||
impl<$Element: Type> sealed::Sealed for $Struct<$Element, DynSize> {}
|
||||
impl<$Element: Value> sealed::Sealed for $Struct<$Element>
|
||||
where
|
||||
$Element::Type: Type<Value = $Element>,
|
||||
{
|
||||
}
|
||||
|
||||
impl<$Element: Type> PortType for $Struct<$Element, DynSize> {
|
||||
impl<$Element: Value> PortType for $Struct<$Element>
|
||||
where
|
||||
$Element::Type: Type<Value = $Element>,
|
||||
{
|
||||
type PortKindTy = ();
|
||||
type Port = Self;
|
||||
type PortType = <Self as ToExpr>::Type;
|
||||
type PortValue = Self;
|
||||
|
||||
fn port_kind(_port_kind: Self::PortKindTy) -> PortKind {
|
||||
Self::PORT_KIND
|
||||
|
@ -152,14 +164,18 @@ macro_rules! impl_port_struct {
|
|||
}
|
||||
}
|
||||
|
||||
impl<$Element: Type> PortStruct for $Struct<$Element, DynSize> {
|
||||
impl<$Element: Value> PortStruct for $Struct<$Element>
|
||||
where
|
||||
$Element::Type: Type<Value = $Element>,
|
||||
{
|
||||
type Element = $Element;
|
||||
type ElementType = $Element::Type;
|
||||
|
||||
fn addr(this: Expr<Self>) -> Expr<UInt> {
|
||||
fn addr(this: Expr<Self>) -> Expr<DynUInt> {
|
||||
this.addr
|
||||
}
|
||||
|
||||
fn en(this: Expr<Self>) -> Expr<Bool> {
|
||||
fn en(this: Expr<Self>) -> Expr<UInt<1>> {
|
||||
this.en
|
||||
}
|
||||
|
||||
|
@ -174,9 +190,14 @@ macro_rules! impl_port_struct {
|
|||
|
||||
impl_port_struct! {
|
||||
impl<Element> _ for ReadStruct {
|
||||
fn port_ty(port: &MemPort<Self>) -> Self {
|
||||
let element = Element::from_canonical(port.mem_element_type);
|
||||
ReadStruct[element][port.addr_type.width()]
|
||||
fn port_ty(port: &MemPort<Self>) -> <MemPort<Self> as ToExpr>::Type {
|
||||
type T<V> = <V as ToExpr>::Type;
|
||||
T::<Self> {
|
||||
addr: port.addr_type,
|
||||
en: UIntType::new(),
|
||||
clk: ClockType,
|
||||
data: Element::Type::from_dyn_canonical_type(port.mem_element_type),
|
||||
}
|
||||
}
|
||||
|
||||
const PORT_KIND: PortKind = PortKind::ReadOnly;
|
||||
|
@ -193,7 +214,7 @@ impl_port_struct! {
|
|||
None
|
||||
}
|
||||
|
||||
fn wmode(_this: Expr<Self>) -> Expr<Bool> {
|
||||
fn wmode(_this: Expr<Self>) -> Expr<UInt<1>> {
|
||||
false.to_expr()
|
||||
}
|
||||
}
|
||||
|
@ -201,9 +222,16 @@ impl_port_struct! {
|
|||
|
||||
impl_port_struct! {
|
||||
impl<Element> _ for WriteStruct {
|
||||
fn port_ty(port: &MemPort<Self>) -> Self {
|
||||
let element = Element::from_canonical(port.mem_element_type);
|
||||
WriteStruct[element][port.addr_type.width()]
|
||||
fn port_ty(port: &MemPort<Self>) -> <MemPort<Self> as ToExpr>::Type {
|
||||
type T<V> = <V as ToExpr>::Type;
|
||||
let element_ty = Element::Type::from_dyn_canonical_type(port.mem_element_type);
|
||||
T::<Self> {
|
||||
addr: port.addr_type,
|
||||
en: UIntType::new(),
|
||||
clk: ClockType,
|
||||
mask: element_ty.mask_type(),
|
||||
data: element_ty,
|
||||
}
|
||||
}
|
||||
|
||||
const PORT_KIND: PortKind = PortKind::WriteOnly;
|
||||
|
@ -220,7 +248,7 @@ impl_port_struct! {
|
|||
Some(this.mask)
|
||||
}
|
||||
|
||||
fn wmode(_this: Expr<Self>) -> Expr<Bool> {
|
||||
fn wmode(_this: Expr<Self>) -> Expr<UInt<1>> {
|
||||
true.to_expr()
|
||||
}
|
||||
}
|
||||
|
@ -228,9 +256,18 @@ impl_port_struct! {
|
|||
|
||||
impl_port_struct! {
|
||||
impl<Element> _ for ReadWriteStruct {
|
||||
fn port_ty(port: &MemPort<Self>) -> Self {
|
||||
let element = Element::from_canonical(port.mem_element_type);
|
||||
ReadWriteStruct[element][port.addr_type.width()]
|
||||
fn port_ty(port: &MemPort<Self>) -> <MemPort<Self> as ToExpr>::Type {
|
||||
type T<V> = <V as ToExpr>::Type;
|
||||
let element_ty = Element::Type::from_dyn_canonical_type(port.mem_element_type);
|
||||
T::<Self> {
|
||||
addr: port.addr_type,
|
||||
en: UIntType::new(),
|
||||
clk: ClockType,
|
||||
rdata: element_ty.clone(),
|
||||
wmode: UIntType::new(),
|
||||
wmask: element_ty.mask_type(),
|
||||
wdata: element_ty,
|
||||
}
|
||||
}
|
||||
|
||||
const PORT_KIND: PortKind = PortKind::ReadWrite;
|
||||
|
@ -247,7 +284,7 @@ impl_port_struct! {
|
|||
Some(this.wmask)
|
||||
}
|
||||
|
||||
fn wmode(this: Expr<Self>) -> Expr<Bool> {
|
||||
fn wmode(this: Expr<Self>) -> Expr<UInt<1>> {
|
||||
this.wmode
|
||||
}
|
||||
}
|
||||
|
@ -345,31 +382,42 @@ pub struct MemPort<T: PortType> {
|
|||
source_location: SourceLocation,
|
||||
port_kind: T::PortKindTy,
|
||||
port_index: usize,
|
||||
addr_type: UInt,
|
||||
mem_element_type: CanonicalType,
|
||||
addr_type: DynUIntType,
|
||||
mem_element_type: Interned<dyn DynCanonicalType>,
|
||||
}
|
||||
|
||||
impl<T: PortType> fmt::Debug for MemPort<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("MemPort(")?;
|
||||
self.mem_name.fmt(f)?;
|
||||
f.write_str(".")?;
|
||||
self.port_name().fmt(f)?;
|
||||
f.write_str(": ")?;
|
||||
match self.port_kind() {
|
||||
PortKind::ReadOnly => f.write_str("ReadStruct<")?,
|
||||
PortKind::WriteOnly => f.write_str("WriteStruct<")?,
|
||||
PortKind::ReadWrite => f.write_str("ReadWriteStruct<")?,
|
||||
}
|
||||
self.mem_element_type.fmt(f)?;
|
||||
f.write_str(">)")
|
||||
let Self {
|
||||
mem_name,
|
||||
source_location: _,
|
||||
port_kind: _,
|
||||
port_index: _,
|
||||
addr_type,
|
||||
mem_element_type,
|
||||
} = self;
|
||||
f.debug_struct("MemPort")
|
||||
.field("mem_name", mem_name)
|
||||
.field("port_name", &self.port_name())
|
||||
.field("addr_type", addr_type)
|
||||
.field("mem_element_type", mem_element_type)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PortType> ToExpr for MemPort<T> {
|
||||
type Type = T::PortType;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
T::port_ty(self)
|
||||
}
|
||||
|
||||
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
|
||||
Expr::new_unchecked(self.expr_enum())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PortType> MemPort<T> {
|
||||
pub fn ty(&self) -> T::Port {
|
||||
T::port_ty(self)
|
||||
}
|
||||
pub fn source_location(&self) -> SourceLocation {
|
||||
self.source_location
|
||||
}
|
||||
|
@ -388,10 +436,10 @@ impl<T: PortType> MemPort<T> {
|
|||
index: self.port_index,
|
||||
}
|
||||
}
|
||||
pub fn mem_element_type(&self) -> CanonicalType {
|
||||
pub fn mem_element_type(&self) -> Interned<dyn DynCanonicalType> {
|
||||
self.mem_element_type
|
||||
}
|
||||
pub fn addr_type(&self) -> UInt {
|
||||
pub fn addr_type(&self) -> DynUIntType {
|
||||
self.addr_type
|
||||
}
|
||||
pub fn canonical(&self) -> MemPort<DynPortType> {
|
||||
|
@ -415,6 +463,7 @@ impl<T: PortType> MemPort<T> {
|
|||
pub fn from_canonical(port: MemPort<DynPortType>) -> Self
|
||||
where
|
||||
T: PortStruct,
|
||||
T::Type: BundleType<Value = T>,
|
||||
{
|
||||
let MemPort {
|
||||
mem_name,
|
||||
|
@ -439,8 +488,8 @@ impl<T: PortType> MemPort<T> {
|
|||
mem_name: ScopedNameId,
|
||||
source_location: SourceLocation,
|
||||
port_name: PortName,
|
||||
addr_type: UInt,
|
||||
mem_element_type: CanonicalType,
|
||||
addr_type: DynUIntType,
|
||||
mem_element_type: Interned<dyn DynCanonicalType>,
|
||||
) -> Self {
|
||||
assert!(
|
||||
mem_element_type.is_storable(),
|
||||
|
@ -471,10 +520,10 @@ pub enum ReadUnderWrite {
|
|||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
struct MemImpl<Element: Type, Len: Size, P> {
|
||||
struct MemImpl<T: ArrayTypeTrait, P> {
|
||||
scoped_name: ScopedNameId,
|
||||
source_location: SourceLocation,
|
||||
array_type: ArrayType<Element, Len>,
|
||||
array_type: T,
|
||||
initial_value: Option<Interned<BitSlice>>,
|
||||
ports: P,
|
||||
read_latency: usize,
|
||||
|
@ -484,11 +533,11 @@ struct MemImpl<Element: Type, Len: Size, P> {
|
|||
mem_annotations: Interned<[Annotation]>,
|
||||
}
|
||||
|
||||
pub struct Mem<Element: Type = CanonicalType, Len: Size = DynSize>(
|
||||
Interned<MemImpl<Element, Len, Interned<[MemPort<DynPortType>]>>>,
|
||||
pub struct Mem<VA: ValueArrayOrSlice + ?Sized>(
|
||||
Interned<MemImpl<ArrayType<VA>, Interned<[Interned<MemPort<DynPortType>>]>>>,
|
||||
);
|
||||
|
||||
struct PortsDebug<'a>(&'a [MemPort<DynPortType>]);
|
||||
struct PortsDebug<'a>(&'a [Interned<MemPort<DynPortType>>]);
|
||||
|
||||
impl fmt::Debug for PortsDebug<'_> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
|
@ -502,7 +551,7 @@ impl fmt::Debug for PortsDebug<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<Element: Type, Len: Size> fmt::Debug for Mem<Element, Len> {
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> fmt::Debug for Mem<VA> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let MemImpl {
|
||||
scoped_name,
|
||||
|
@ -530,37 +579,37 @@ impl<Element: Type, Len: Size> fmt::Debug for Mem<Element, Len> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<Element: Type, Len: Size> Hash for Mem<Element, Len> {
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Hash for Mem<VA> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.0.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl<Element: Type, Len: Size> Eq for Mem<Element, Len> {}
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Eq for Mem<VA> {}
|
||||
|
||||
impl<Element: Type, Len: Size> PartialEq for Mem<Element, Len> {
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> PartialEq for Mem<VA> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.0 == other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<Element: Type, Len: Size> Copy for Mem<Element, Len> {}
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Copy for Mem<VA> {}
|
||||
|
||||
impl<Element: Type, Len: Size> Clone for Mem<Element, Len> {
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Clone for Mem<VA> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl<Element: Type, Len: Size> Mem<Element, Len> {
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> Mem<VA> {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[track_caller]
|
||||
pub fn new_unchecked(
|
||||
scoped_name: ScopedNameId,
|
||||
source_location: SourceLocation,
|
||||
array_type: ArrayType<Element, Len>,
|
||||
array_type: ArrayType<VA>,
|
||||
initial_value: Option<Interned<BitSlice>>,
|
||||
ports: Interned<[MemPort<DynPortType>]>,
|
||||
ports: Interned<[Interned<MemPort<DynPortType>>]>,
|
||||
read_latency: usize,
|
||||
write_latency: NonZeroU32,
|
||||
read_under_write: ReadUnderWrite,
|
||||
|
@ -568,14 +617,14 @@ impl<Element: Type, Len: Size> Mem<Element, Len> {
|
|||
mem_annotations: Interned<[Annotation]>,
|
||||
) -> Self {
|
||||
if let Some(initial_value) = initial_value {
|
||||
MemBuilder::<Element, Len>::check_initial_value_bit_slice(
|
||||
MemBuilder::<VA>::check_initial_value_bit_slice(
|
||||
array_type.element(),
|
||||
Some(array_type.len()),
|
||||
initial_value,
|
||||
);
|
||||
}
|
||||
let addr_width = memory_addr_width(array_type.len());
|
||||
let expected_mem_element_type = array_type.element().canonical();
|
||||
let expected_mem_element_type = array_type.element().canonical_dyn();
|
||||
assert!(
|
||||
expected_mem_element_type.is_storable(),
|
||||
"memory element type must be a storable type"
|
||||
|
@ -588,7 +637,7 @@ impl<Element: Type, Len: Size> Mem<Element, Len> {
|
|||
port_index,
|
||||
addr_type,
|
||||
mem_element_type,
|
||||
} = *port;
|
||||
} = **port;
|
||||
assert_eq!(mem_name, scoped_name, "memory name must match with ports");
|
||||
assert_eq!(
|
||||
port_index, index,
|
||||
|
@ -610,7 +659,7 @@ impl<Element: Type, Len: Size> Mem<Element, Len> {
|
|||
};
|
||||
assert_eq!(
|
||||
Some(port),
|
||||
ports.get(port.port_index),
|
||||
ports.get(port.port_index).map(|v| &**v),
|
||||
"port on memory must match annotation's target base"
|
||||
);
|
||||
}
|
||||
|
@ -633,13 +682,13 @@ impl<Element: Type, Len: Size> Mem<Element, Len> {
|
|||
pub fn source_location(self) -> SourceLocation {
|
||||
self.0.source_location
|
||||
}
|
||||
pub fn array_type(self) -> ArrayType<Element, Len> {
|
||||
self.0.array_type
|
||||
pub fn array_type(self) -> ArrayType<VA> {
|
||||
self.0.array_type.clone()
|
||||
}
|
||||
pub fn initial_value(self) -> Option<Interned<BitSlice>> {
|
||||
self.0.initial_value
|
||||
}
|
||||
pub fn ports(self) -> Interned<[MemPort<DynPortType>]> {
|
||||
pub fn ports(self) -> Interned<[Interned<MemPort<DynPortType>>]> {
|
||||
self.0.ports
|
||||
}
|
||||
pub fn read_latency(self) -> usize {
|
||||
|
@ -657,7 +706,7 @@ impl<Element: Type, Len: Size> Mem<Element, Len> {
|
|||
pub fn mem_annotations(self) -> Interned<[Annotation]> {
|
||||
self.0.mem_annotations
|
||||
}
|
||||
pub fn canonical(self) -> Mem {
|
||||
pub fn canonical(self) -> Mem<[DynCanonicalValue]> {
|
||||
let MemImpl {
|
||||
scoped_name,
|
||||
source_location,
|
||||
|
@ -670,7 +719,7 @@ impl<Element: Type, Len: Size> Mem<Element, Len> {
|
|||
port_annotations,
|
||||
mem_annotations,
|
||||
} = *self.0;
|
||||
let array_type = array_type.as_dyn_array();
|
||||
let array_type = array_type.canonical();
|
||||
Mem(Intern::intern_sized(MemImpl {
|
||||
scoped_name,
|
||||
source_location,
|
||||
|
@ -702,10 +751,10 @@ impl<T: fmt::Debug> fmt::Debug for MaybeSpecified<T> {
|
|||
pub(crate) struct MemBuilderTarget {
|
||||
pub(crate) scoped_name: ScopedNameId,
|
||||
pub(crate) source_location: SourceLocation,
|
||||
pub(crate) mem_element_type: CanonicalType,
|
||||
pub(crate) mem_element_type: Interned<dyn DynCanonicalType>,
|
||||
pub(crate) depth: Option<usize>,
|
||||
pub(crate) initial_value: Option<Interned<BitSlice>>,
|
||||
pub(crate) ports: Vec<MemPort<DynPortType>>,
|
||||
pub(crate) ports: Vec<Interned<MemPort<DynPortType>>>,
|
||||
pub(crate) read_latency: usize,
|
||||
pub(crate) write_latency: NonZeroU32,
|
||||
pub(crate) read_under_write: ReadUnderWrite,
|
||||
|
@ -714,11 +763,11 @@ pub(crate) struct MemBuilderTarget {
|
|||
}
|
||||
|
||||
impl MemBuilderTarget {
|
||||
pub(crate) fn make_memory(&self) -> Option<Mem> {
|
||||
pub(crate) fn make_memory(&self) -> Option<Mem<[DynCanonicalValue]>> {
|
||||
Some(Mem::new_unchecked(
|
||||
self.scoped_name,
|
||||
self.source_location,
|
||||
ArrayType::new_dyn(self.mem_element_type, self.depth?),
|
||||
ArrayType::new_slice(self.mem_element_type, self.depth?),
|
||||
self.initial_value,
|
||||
Intern::intern(&self.ports),
|
||||
self.read_latency,
|
||||
|
@ -768,18 +817,16 @@ impl fmt::Debug for MemBuilderTarget {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct MemBuilder<Element: Type, Len: Size = DynSize> {
|
||||
mem_element_type: Element,
|
||||
pub struct MemBuilder<VA: ValueArrayOrSlice + ?Sized> {
|
||||
mem_element_type: VA::ElementType,
|
||||
target: Rc<RefCell<MemBuilderTarget>>,
|
||||
_phantom: PhantomData<Len>,
|
||||
}
|
||||
|
||||
impl<Element: Type, Len: Size> fmt::Debug for MemBuilder<Element, Len> {
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> fmt::Debug for MemBuilder<VA> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
let Self {
|
||||
mem_element_type,
|
||||
target,
|
||||
_phantom: _,
|
||||
} = &self;
|
||||
target.borrow().debug_fmt("MemBuilder", mem_element_type, f)
|
||||
}
|
||||
|
@ -791,16 +838,15 @@ pub fn memory_addr_width(depth: usize) -> usize {
|
|||
.map_or(usize::BITS, usize::ilog2) as usize
|
||||
}
|
||||
|
||||
impl<Element: Type, Len: Size> MemBuilder<Element, Len> {
|
||||
impl<VA: ValueArrayOrSlice + ?Sized> MemBuilder<VA> {
|
||||
#[track_caller]
|
||||
fn check_initial_value_bit_slice(
|
||||
mem_element_type: Element,
|
||||
mem_element_type: &VA::ElementType,
|
||||
depth: Option<usize>,
|
||||
initial_value: Interned<BitSlice>,
|
||||
) -> Interned<BitSlice> {
|
||||
let element_bit_width = mem_element_type.canonical().bit_width();
|
||||
if let Some(depth) = depth {
|
||||
let expected_len = depth.checked_mul(element_bit_width).expect(
|
||||
let expected_len = depth.checked_mul(mem_element_type.bit_width()).expect(
|
||||
"memory must be small enough that its initializer bit length fits in usize",
|
||||
);
|
||||
assert_eq!(
|
||||
|
@ -812,7 +858,7 @@ impl<Element: Type, Len: Size> MemBuilder<Element, Len> {
|
|||
assert!(
|
||||
initial_value
|
||||
.len()
|
||||
.checked_rem(element_bit_width)
|
||||
.checked_rem(mem_element_type.bit_width())
|
||||
.unwrap_or(initial_value.len())
|
||||
== 0,
|
||||
"Mem's initializer bit length must be a multiple of the element type's bit width",
|
||||
|
@ -821,14 +867,14 @@ impl<Element: Type, Len: Size> MemBuilder<Element, Len> {
|
|||
}
|
||||
#[track_caller]
|
||||
fn check_initial_value_expr(
|
||||
mem_element_type: &Element,
|
||||
mem_element_type: &VA::ElementType,
|
||||
depth: Option<usize>,
|
||||
initial_value: Expr<Array>,
|
||||
initial_value: Expr<Array<[DynCanonicalValue]>>,
|
||||
) -> Interned<BitSlice> {
|
||||
let initial_value_ty = Expr::ty(initial_value);
|
||||
let initial_value_ty = initial_value.canonical_type();
|
||||
assert_eq!(
|
||||
*mem_element_type,
|
||||
Element::from_canonical(initial_value_ty.element()),
|
||||
<VA::ElementType as DynType>::from_dyn_canonical_type(*initial_value_ty.element()),
|
||||
"Mem's element type must match initializer's element type",
|
||||
);
|
||||
if let Some(depth) = depth {
|
||||
|
@ -843,7 +889,7 @@ impl<Element: Type, Len: Size> MemBuilder<Element, Len> {
|
|||
};
|
||||
debug_assert_eq!(
|
||||
retval.len(),
|
||||
initial_value_ty.type_properties().bit_width,
|
||||
initial_value_ty.bit_width(),
|
||||
"initial value produced wrong literal bits length"
|
||||
);
|
||||
retval
|
||||
|
@ -852,9 +898,9 @@ impl<Element: Type, Len: Size> MemBuilder<Element, Len> {
|
|||
pub(crate) fn new(
|
||||
scoped_name: ScopedNameId,
|
||||
source_location: SourceLocation,
|
||||
mem_element_type: Element,
|
||||
mem_element_type: VA::ElementType,
|
||||
) -> (Self, Rc<RefCell<MemBuilderTarget>>) {
|
||||
let canonical_mem_element_type = mem_element_type.canonical();
|
||||
let canonical_mem_element_type = mem_element_type.canonical_dyn();
|
||||
assert!(
|
||||
canonical_mem_element_type.is_storable(),
|
||||
"memory element type must be a storable type"
|
||||
|
@ -863,7 +909,7 @@ impl<Element: Type, Len: Size> MemBuilder<Element, Len> {
|
|||
scoped_name,
|
||||
source_location,
|
||||
mem_element_type: canonical_mem_element_type,
|
||||
depth: Len::KNOWN_VALUE,
|
||||
depth: VA::FIXED_LEN_TYPE.map(VA::len_from_len_type),
|
||||
initial_value: None,
|
||||
ports: vec![],
|
||||
read_latency: 0,
|
||||
|
@ -876,7 +922,6 @@ impl<Element: Type, Len: Size> MemBuilder<Element, Len> {
|
|||
Self {
|
||||
mem_element_type,
|
||||
target: Rc::clone(&target),
|
||||
_phantom: PhantomData,
|
||||
},
|
||||
target,
|
||||
)
|
||||
|
@ -886,19 +931,19 @@ impl<Element: Type, Len: Size> MemBuilder<Element, Len> {
|
|||
&mut self,
|
||||
source_location: SourceLocation,
|
||||
port_kind: PortKind,
|
||||
) -> MemPort<DynPortType> {
|
||||
) -> Interned<MemPort<DynPortType>> {
|
||||
let mut target = self.target.borrow_mut();
|
||||
let Some(depth) = target.depth else {
|
||||
panic!("MemBuilder::depth must be called before adding ports");
|
||||
};
|
||||
let port = MemPort {
|
||||
let port = Intern::intern_sized(MemPort {
|
||||
mem_name: target.scoped_name,
|
||||
source_location,
|
||||
port_kind,
|
||||
port_index: target.ports.len(),
|
||||
addr_type: UInt::new(memory_addr_width(depth)),
|
||||
addr_type: DynUIntType::new(memory_addr_width(depth)),
|
||||
mem_element_type: target.mem_element_type,
|
||||
};
|
||||
});
|
||||
target.ports.push(port);
|
||||
port
|
||||
}
|
||||
|
@ -907,53 +952,50 @@ impl<Element: Type, Len: Size> MemBuilder<Element, Len> {
|
|||
&mut self,
|
||||
source_location: SourceLocation,
|
||||
kind: PortKind,
|
||||
) -> Expr<Bundle> {
|
||||
self.new_port_impl(source_location, kind).to_expr()
|
||||
) -> Expr<DynBundle> {
|
||||
Expr::new_unchecked(ExprEnum::MemPort(self.new_port_impl(source_location, kind)))
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn new_port(&mut self, kind: PortKind) -> Expr<Bundle> {
|
||||
pub fn new_port(&mut self, kind: PortKind) -> Expr<DynBundle> {
|
||||
self.new_port_with_loc(SourceLocation::caller(), kind)
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn new_read_port_with_loc(
|
||||
&mut self,
|
||||
source_location: SourceLocation,
|
||||
) -> Expr<ReadStruct<Element, DynSize>> {
|
||||
Expr::from_bundle(
|
||||
self.new_port_impl(source_location, PortKind::ReadOnly)
|
||||
.to_expr(),
|
||||
)
|
||||
) -> Expr<ReadStruct<VA::Element>> {
|
||||
Expr::new_unchecked(ExprEnum::MemPort(
|
||||
self.new_port_impl(source_location, PortKind::ReadOnly),
|
||||
))
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn new_read_port(&mut self) -> Expr<ReadStruct<Element, DynSize>> {
|
||||
pub fn new_read_port(&mut self) -> Expr<ReadStruct<VA::Element>> {
|
||||
self.new_read_port_with_loc(SourceLocation::caller())
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn new_write_port_with_loc(
|
||||
&mut self,
|
||||
source_location: SourceLocation,
|
||||
) -> Expr<WriteStruct<Element, DynSize>> {
|
||||
Expr::from_bundle(
|
||||
self.new_port_impl(source_location, PortKind::WriteOnly)
|
||||
.to_expr(),
|
||||
)
|
||||
) -> Expr<WriteStruct<VA::Element>> {
|
||||
Expr::new_unchecked(ExprEnum::MemPort(
|
||||
self.new_port_impl(source_location, PortKind::WriteOnly),
|
||||
))
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn new_write_port(&mut self) -> Expr<WriteStruct<Element, DynSize>> {
|
||||
pub fn new_write_port(&mut self) -> Expr<WriteStruct<VA::Element>> {
|
||||
self.new_write_port_with_loc(SourceLocation::caller())
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn new_rw_port_with_loc(
|
||||
&mut self,
|
||||
source_location: SourceLocation,
|
||||
) -> Expr<ReadWriteStruct<Element, DynSize>> {
|
||||
Expr::from_bundle(
|
||||
self.new_port_impl(source_location, PortKind::ReadWrite)
|
||||
.to_expr(),
|
||||
)
|
||||
) -> Expr<ReadWriteStruct<VA::Element>> {
|
||||
Expr::new_unchecked(ExprEnum::MemPort(
|
||||
self.new_port_impl(source_location, PortKind::ReadWrite),
|
||||
))
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn new_rw_port(&mut self) -> Expr<ReadWriteStruct<Element, DynSize>> {
|
||||
pub fn new_rw_port(&mut self) -> Expr<ReadWriteStruct<VA::Element>> {
|
||||
self.new_rw_port_with_loc(SourceLocation::caller())
|
||||
}
|
||||
pub fn scoped_name(&self) -> ScopedNameId {
|
||||
|
@ -962,7 +1004,7 @@ impl<Element: Type, Len: Size> MemBuilder<Element, Len> {
|
|||
pub fn source_location(&self) -> SourceLocation {
|
||||
self.target.borrow().source_location
|
||||
}
|
||||
pub fn get_mem_element_type(&self) -> &Element {
|
||||
pub fn get_mem_element_type(&self) -> &VA::ElementType {
|
||||
&self.mem_element_type
|
||||
}
|
||||
#[allow(clippy::result_unit_err)]
|
||||
|
@ -985,28 +1027,28 @@ impl<Element: Type, Len: Size> MemBuilder<Element, Len> {
|
|||
target.depth = Some(depth);
|
||||
}
|
||||
#[allow(clippy::result_unit_err)]
|
||||
pub fn get_array_type(&self) -> Result<ArrayType<Element, Len>, ()> {
|
||||
Ok(ArrayType::new(
|
||||
self.mem_element_type,
|
||||
Len::from_usize(self.get_depth()?),
|
||||
pub fn get_array_type(&self) -> Result<ArrayType<VA>, ()> {
|
||||
Ok(ArrayType::new_with_len(
|
||||
self.mem_element_type.clone(),
|
||||
self.get_depth()?,
|
||||
))
|
||||
}
|
||||
pub fn get_initial_value(&self) -> Option<Interned<BitSlice>> {
|
||||
self.target.borrow().initial_value
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn initial_value(&mut self, initial_value: impl ToExpr<Type = ArrayType<Element, Len>>) {
|
||||
pub fn initial_value(&mut self, initial_value: impl ToExpr<Type = ArrayType<VA>>) {
|
||||
let mut target = self.target.borrow_mut();
|
||||
if target.initial_value.is_some() {
|
||||
panic!("can't set Mem's initial value more than once");
|
||||
}
|
||||
let initial_value = Expr::as_dyn_array(initial_value.to_expr());
|
||||
let initial_value = initial_value.to_expr().canonical();
|
||||
target.initial_value = Some(Self::check_initial_value_expr(
|
||||
&self.mem_element_type,
|
||||
target.depth,
|
||||
initial_value,
|
||||
));
|
||||
target.depth = Some(Expr::ty(initial_value).len());
|
||||
target.depth = Some(initial_value.ty().len());
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn initial_value_bit_slice(&mut self, initial_value: Interned<BitSlice>) {
|
||||
|
@ -1015,11 +1057,11 @@ impl<Element: Type, Len: Size> MemBuilder<Element, Len> {
|
|||
panic!("can't set Mem's initial value more than once");
|
||||
}
|
||||
target.initial_value = Some(Self::check_initial_value_bit_slice(
|
||||
self.mem_element_type,
|
||||
&self.mem_element_type,
|
||||
target.depth,
|
||||
initial_value,
|
||||
));
|
||||
let element_bit_width = self.mem_element_type.canonical().bit_width();
|
||||
let element_bit_width = self.mem_element_type.bit_width();
|
||||
if element_bit_width != 0 {
|
||||
target.depth = Some(initial_value.len() / element_bit_width);
|
||||
}
|
||||
|
@ -1050,32 +1092,3 @@ impl<Element: Type, Len: Size> MemBuilder<Element, Len> {
|
|||
.extend(annotations.into_annotations());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn splat_mask<T: Type>(ty: T, value: Expr<Bool>) -> Expr<AsMask<T>> {
|
||||
let canonical_ty = ty.canonical();
|
||||
match canonical_ty {
|
||||
CanonicalType::UInt(_)
|
||||
| CanonicalType::SInt(_)
|
||||
| CanonicalType::Bool(_)
|
||||
| CanonicalType::AsyncReset(_)
|
||||
| CanonicalType::SyncReset(_)
|
||||
| CanonicalType::Reset(_)
|
||||
| CanonicalType::Clock(_)
|
||||
| CanonicalType::Enum(_) => Expr::from_canonical(Expr::canonical(value)),
|
||||
CanonicalType::Array(array) => Expr::from_canonical(Expr::canonical(repeat(
|
||||
splat_mask(array.element(), value),
|
||||
array.len(),
|
||||
))),
|
||||
CanonicalType::Bundle(bundle) => Expr::from_canonical(Expr::canonical(
|
||||
BundleLiteral::new(
|
||||
bundle.mask_type(),
|
||||
bundle
|
||||
.fields()
|
||||
.iter()
|
||||
.map(|field| splat_mask(field.ty, value))
|
||||
.collect(),
|
||||
)
|
||||
.to_expr(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -2,18 +2,18 @@
|
|||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
annotations::TargetedAnnotation,
|
||||
array::Array,
|
||||
bundle::{Bundle, BundleType},
|
||||
expr::{CastBitsTo, CastToBits, Expr, ExprEnum, ToExpr},
|
||||
int::{Bool, SInt, Size, UInt},
|
||||
array::{Array, ArrayType, ValueArrayOrSlice},
|
||||
bundle::{BundleType, BundleValue, DynBundle},
|
||||
expr::{Expr, ExprEnum, ToExpr},
|
||||
int::{DynSInt, DynSIntType, DynUInt, DynUIntType},
|
||||
intern::{Intern, Interned},
|
||||
memory::{Mem, MemPort, PortType},
|
||||
module::{
|
||||
transform::visit::{Fold, Folder},
|
||||
Block, Id, Module, NameId, ScopedNameId, Stmt, StmtConnect, StmtWire,
|
||||
Block, Module, NameId, NameIdGen, ScopedNameId, Stmt, StmtConnect, StmtWire,
|
||||
},
|
||||
source_location::SourceLocation,
|
||||
ty::{CanonicalType, Type},
|
||||
ty::{DynCanonicalValue, DynType, Type, TypeEnum},
|
||||
util::MakeMutSlice,
|
||||
wire::Wire,
|
||||
};
|
||||
|
@ -28,31 +28,26 @@ use std::{
|
|||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
enum SingleType {
|
||||
UInt(UInt),
|
||||
SInt(SInt),
|
||||
Bool(Bool),
|
||||
UIntArray(Array<UInt>),
|
||||
SIntArray(Array<SInt>),
|
||||
BoolArray(Array<Bool>),
|
||||
UInt(DynUIntType),
|
||||
SInt(DynSIntType),
|
||||
UIntArray(ArrayType<[DynUInt]>),
|
||||
SIntArray(ArrayType<[DynSInt]>),
|
||||
}
|
||||
|
||||
impl SingleType {
|
||||
fn is_array_type(self, array_type: Array) -> bool {
|
||||
fn is_array_type(self, array_type: ArrayType<[DynCanonicalValue]>) -> bool {
|
||||
match self {
|
||||
SingleType::UInt(_) | SingleType::SInt(_) | SingleType::Bool(_) => false,
|
||||
SingleType::UIntArray(ty) => ty.as_dyn_array() == array_type,
|
||||
SingleType::SIntArray(ty) => ty.as_dyn_array() == array_type,
|
||||
SingleType::BoolArray(ty) => ty.as_dyn_array() == array_type,
|
||||
SingleType::UInt(_) | SingleType::SInt(_) => false,
|
||||
SingleType::UIntArray(ty) => ty.canonical() == array_type,
|
||||
SingleType::SIntArray(ty) => ty.canonical() == array_type,
|
||||
}
|
||||
}
|
||||
fn array_len(self) -> usize {
|
||||
match self {
|
||||
SingleType::UInt(_ty) => 1,
|
||||
SingleType::SInt(_ty) => 1,
|
||||
SingleType::Bool(_ty) => 1,
|
||||
SingleType::UIntArray(ty) => ty.len(),
|
||||
SingleType::SIntArray(ty) => ty.len(),
|
||||
SingleType::BoolArray(ty) => ty.len(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +58,7 @@ enum MemSplit {
|
|||
fields: Rc<[MemSplit]>,
|
||||
},
|
||||
Single {
|
||||
output_mem: Option<Mem>,
|
||||
output_mem: Option<Mem<[DynCanonicalValue]>>,
|
||||
element_type: SingleType,
|
||||
unchanged_element_type: bool,
|
||||
},
|
||||
|
@ -88,17 +83,17 @@ impl MemSplit {
|
|||
MemSplit::Array { elements: _ } => self,
|
||||
}
|
||||
}
|
||||
fn new(element_type: CanonicalType) -> Self {
|
||||
fn new(element_type: TypeEnum) -> Self {
|
||||
match element_type {
|
||||
CanonicalType::Bundle(bundle_ty) => MemSplit::Bundle {
|
||||
TypeEnum::BundleType(bundle_ty) => MemSplit::Bundle {
|
||||
fields: bundle_ty
|
||||
.fields()
|
||||
.into_iter()
|
||||
.map(|field| Self::new(field.ty).mark_changed_element_type())
|
||||
.map(|field| Self::new(field.ty.type_enum()).mark_changed_element_type())
|
||||
.collect(),
|
||||
},
|
||||
CanonicalType::Array(ty) => {
|
||||
let element = MemSplit::new(ty.element());
|
||||
TypeEnum::ArrayType(ty) => {
|
||||
let element = MemSplit::new(ty.element().type_enum());
|
||||
if let Self::Single {
|
||||
output_mem: _,
|
||||
element_type,
|
||||
|
@ -108,7 +103,7 @@ impl MemSplit {
|
|||
match element_type {
|
||||
SingleType::UInt(element_type) => Self::Single {
|
||||
output_mem: None,
|
||||
element_type: SingleType::UIntArray(Array::new_dyn(
|
||||
element_type: SingleType::UIntArray(ArrayType::new_slice(
|
||||
element_type,
|
||||
ty.len(),
|
||||
)),
|
||||
|
@ -116,15 +111,7 @@ impl MemSplit {
|
|||
},
|
||||
SingleType::SInt(element_type) => Self::Single {
|
||||
output_mem: None,
|
||||
element_type: SingleType::SIntArray(Array::new_dyn(
|
||||
element_type,
|
||||
ty.len(),
|
||||
)),
|
||||
unchanged_element_type,
|
||||
},
|
||||
SingleType::Bool(element_type) => Self::Single {
|
||||
output_mem: None,
|
||||
element_type: SingleType::BoolArray(Array::new_dyn(
|
||||
element_type: SingleType::SIntArray(ArrayType::new_slice(
|
||||
element_type,
|
||||
ty.len(),
|
||||
)),
|
||||
|
@ -132,8 +119,8 @@ impl MemSplit {
|
|||
},
|
||||
SingleType::UIntArray(element_type) => Self::Single {
|
||||
output_mem: None,
|
||||
element_type: SingleType::UIntArray(Array::new_dyn(
|
||||
element_type.element(),
|
||||
element_type: SingleType::UIntArray(ArrayType::new_slice(
|
||||
*element_type.element(),
|
||||
ty.len()
|
||||
.checked_mul(element_type.len())
|
||||
.expect("memory element type can't be too big"),
|
||||
|
@ -142,18 +129,8 @@ impl MemSplit {
|
|||
},
|
||||
SingleType::SIntArray(element_type) => Self::Single {
|
||||
output_mem: None,
|
||||
element_type: SingleType::SIntArray(Array::new_dyn(
|
||||
element_type.element(),
|
||||
ty.len()
|
||||
.checked_mul(element_type.len())
|
||||
.expect("memory element type can't be too big"),
|
||||
)),
|
||||
unchanged_element_type: false,
|
||||
},
|
||||
SingleType::BoolArray(element_type) => Self::Single {
|
||||
output_mem: None,
|
||||
element_type: SingleType::BoolArray(Array::new_dyn(
|
||||
element_type.element(),
|
||||
element_type: SingleType::SIntArray(ArrayType::new_slice(
|
||||
*element_type.element(),
|
||||
ty.len()
|
||||
.checked_mul(element_type.len())
|
||||
.expect("memory element type can't be too big"),
|
||||
|
@ -168,30 +145,25 @@ impl MemSplit {
|
|||
}
|
||||
}
|
||||
}
|
||||
CanonicalType::UInt(ty) => Self::Single {
|
||||
TypeEnum::UInt(ty) => Self::Single {
|
||||
output_mem: None,
|
||||
element_type: SingleType::UInt(ty),
|
||||
unchanged_element_type: true,
|
||||
},
|
||||
CanonicalType::SInt(ty) => Self::Single {
|
||||
TypeEnum::SInt(ty) => Self::Single {
|
||||
output_mem: None,
|
||||
element_type: SingleType::SInt(ty),
|
||||
unchanged_element_type: true,
|
||||
},
|
||||
CanonicalType::Bool(ty) => Self::Single {
|
||||
TypeEnum::EnumType(ty) => Self::Single {
|
||||
output_mem: None,
|
||||
element_type: SingleType::Bool(ty),
|
||||
unchanged_element_type: true,
|
||||
},
|
||||
CanonicalType::Enum(ty) => Self::Single {
|
||||
output_mem: None,
|
||||
element_type: SingleType::UInt(UInt::new_dyn(ty.type_properties().bit_width)),
|
||||
element_type: SingleType::UInt(DynUIntType::new(ty.bit_width())),
|
||||
unchanged_element_type: false,
|
||||
},
|
||||
CanonicalType::Clock(_)
|
||||
| CanonicalType::AsyncReset(_)
|
||||
| CanonicalType::SyncReset(_)
|
||||
| CanonicalType::Reset(_) => unreachable!("memory element type is a storable type"),
|
||||
TypeEnum::Clock(_)
|
||||
| TypeEnum::AsyncReset(_)
|
||||
| TypeEnum::SyncReset(_)
|
||||
| TypeEnum::Reset(_) => unreachable!("memory element type is a storable type"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -201,9 +173,9 @@ struct MemState {
|
|||
}
|
||||
|
||||
struct SplitState<'a> {
|
||||
wire_rdata: Box<[Option<Expr<CanonicalType>>]>,
|
||||
wire_wdata: Box<[Option<Expr<CanonicalType>>]>,
|
||||
wire_wmask: Box<[Option<Expr<CanonicalType>>]>,
|
||||
wire_rdata: Box<[Option<Expr<DynCanonicalValue>>]>,
|
||||
wire_wdata: Box<[Option<Expr<DynCanonicalValue>>]>,
|
||||
wire_wmask: Box<[Option<Expr<DynCanonicalValue>>]>,
|
||||
initial_value: Option<Box<[&'a BitSlice]>>,
|
||||
}
|
||||
|
||||
|
@ -251,7 +223,7 @@ impl<'a> SplitStateStack<'a> {
|
|||
}
|
||||
fn push_map(
|
||||
&mut self,
|
||||
mut wire_map: impl FnMut(Expr<CanonicalType>) -> Expr<CanonicalType>,
|
||||
mut wire_map: impl FnMut(Expr<DynCanonicalValue>) -> Expr<DynCanonicalValue>,
|
||||
mut initial_value_element_map: impl FnMut(&BitSlice) -> &BitSlice,
|
||||
) {
|
||||
let top_index = self.top_index + 1;
|
||||
|
@ -288,10 +260,10 @@ impl<'a> SplitStateStack<'a> {
|
|||
|
||||
struct SplitMemState<'a, 'b> {
|
||||
module_state: &'a mut ModuleState,
|
||||
input_mem: Mem,
|
||||
output_mems: &'a mut Vec<Mem>,
|
||||
input_mem: Mem<[DynCanonicalValue]>,
|
||||
output_mems: &'a mut Vec<Mem<[DynCanonicalValue]>>,
|
||||
output_stmts: &'a mut Vec<Stmt>,
|
||||
element_type: CanonicalType,
|
||||
element_type: TypeEnum,
|
||||
split: &'a mut MemSplit,
|
||||
mem_name_path: &'a mut String,
|
||||
split_state_stack: &'a mut SplitStateStack<'b>,
|
||||
|
@ -303,7 +275,7 @@ impl SplitMemState<'_, '_> {
|
|||
let outer_mem_name_path_len = self.mem_name_path.len();
|
||||
match self.split {
|
||||
MemSplit::Bundle { fields } => {
|
||||
let CanonicalType::Bundle(bundle_type) = self.element_type else {
|
||||
let TypeEnum::BundleType(bundle_type) = self.element_type else {
|
||||
unreachable!();
|
||||
};
|
||||
for ((field, field_offset), split) in bundle_type
|
||||
|
@ -317,9 +289,7 @@ impl SplitMemState<'_, '_> {
|
|||
self.mem_name_path.push_str(&field.name);
|
||||
let field_ty_bit_width = field.ty.bit_width();
|
||||
self.split_state_stack.push_map(
|
||||
|e: Expr<CanonicalType>| {
|
||||
Expr::field(Expr::<Bundle>::from_canonical(e), &field.name)
|
||||
},
|
||||
|e: Expr<DynCanonicalValue>| e.with_type::<DynBundle>().field(&field.name),
|
||||
|initial_value_element| {
|
||||
&initial_value_element[field_offset..][..field_ty_bit_width]
|
||||
},
|
||||
|
@ -329,7 +299,7 @@ impl SplitMemState<'_, '_> {
|
|||
input_mem: self.input_mem,
|
||||
output_mems: self.output_mems,
|
||||
output_stmts: self.output_stmts,
|
||||
element_type: field.ty,
|
||||
element_type: field.ty.type_enum(),
|
||||
split,
|
||||
mem_name_path: self.mem_name_path,
|
||||
split_state_stack: self.split_state_stack,
|
||||
|
@ -358,7 +328,7 @@ impl SplitMemState<'_, '_> {
|
|||
.zip(self.mem_state.replacement_ports.iter())
|
||||
{
|
||||
let port_expr = port.to_expr();
|
||||
let wire_expr = Expr::<Bundle>::from_canonical(wire.to_expr());
|
||||
let wire_expr = Expr::<DynBundle>::new_unchecked(*wire);
|
||||
for name in [
|
||||
Some("addr"),
|
||||
Some("clk"),
|
||||
|
@ -370,8 +340,8 @@ impl SplitMemState<'_, '_> {
|
|||
};
|
||||
self.output_stmts.push(
|
||||
StmtConnect {
|
||||
lhs: Expr::field(port_expr, name),
|
||||
rhs: Expr::field(wire_expr, name),
|
||||
lhs: port_expr.field(name),
|
||||
rhs: wire_expr.field(name),
|
||||
source_location: port.source_location(),
|
||||
}
|
||||
.into(),
|
||||
|
@ -382,16 +352,18 @@ impl SplitMemState<'_, '_> {
|
|||
self.output_mems.push(new_mem);
|
||||
}
|
||||
MemSplit::Array { elements } => {
|
||||
let CanonicalType::Array(array_type) = self.element_type else {
|
||||
let TypeEnum::ArrayType(array_type) = self.element_type else {
|
||||
unreachable!();
|
||||
};
|
||||
let element_type = array_type.element();
|
||||
let element_bit_width = element_type.bit_width();
|
||||
let element_type = array_type.element().type_enum();
|
||||
let element_bit_width = array_type.element().bit_width();
|
||||
for (index, split) in elements.make_mut_slice().iter_mut().enumerate() {
|
||||
self.mem_name_path.truncate(outer_mem_name_path_len);
|
||||
write!(self.mem_name_path, "_{index}").unwrap();
|
||||
self.split_state_stack.push_map(
|
||||
|e| Expr::<Array>::from_canonical(e)[index],
|
||||
|e: Expr<DynCanonicalValue>| {
|
||||
e.with_type::<Array<[DynCanonicalValue]>>()[index]
|
||||
},
|
||||
|initial_value_element| {
|
||||
&initial_value_element[index * element_bit_width..][..element_bit_width]
|
||||
},
|
||||
|
@ -416,7 +388,8 @@ impl SplitMemState<'_, '_> {
|
|||
}
|
||||
|
||||
struct ModuleState {
|
||||
output_module: Option<Interned<Module<Bundle>>>,
|
||||
output_module: Option<Interned<Module<DynBundle>>>,
|
||||
name_id_gen: NameIdGen,
|
||||
memories: HashMap<ScopedNameId, MemState>,
|
||||
}
|
||||
|
||||
|
@ -424,18 +397,18 @@ impl ModuleState {
|
|||
#[allow(clippy::too_many_arguments)]
|
||||
fn connect_split_mem_port_arrays(
|
||||
output_stmts: &mut Vec<Stmt>,
|
||||
input_array_types: &[Array],
|
||||
input_array_types: &[ArrayType<[DynCanonicalValue]>],
|
||||
memory_element_array_range_start: usize,
|
||||
memory_element_array_range_len: usize,
|
||||
wire_rdata: Option<Expr<CanonicalType>>,
|
||||
wire_wdata: Option<Expr<CanonicalType>>,
|
||||
wire_wmask: Option<Expr<CanonicalType>>,
|
||||
port_rdata: Option<Expr<Array>>,
|
||||
port_wdata: Option<Expr<Array>>,
|
||||
port_wmask: Option<Expr<Array>>,
|
||||
connect_rdata: impl Copy + Fn(&mut Vec<Stmt>, Expr<CanonicalType>, Expr<CanonicalType>),
|
||||
connect_wdata: impl Copy + Fn(&mut Vec<Stmt>, Expr<CanonicalType>, Expr<CanonicalType>),
|
||||
connect_wmask: impl Copy + Fn(&mut Vec<Stmt>, Expr<CanonicalType>, Expr<CanonicalType>),
|
||||
wire_rdata: Option<Expr<DynCanonicalValue>>,
|
||||
wire_wdata: Option<Expr<DynCanonicalValue>>,
|
||||
wire_wmask: Option<Expr<DynCanonicalValue>>,
|
||||
port_rdata: Option<Expr<Array<[DynCanonicalValue]>>>,
|
||||
port_wdata: Option<Expr<Array<[DynCanonicalValue]>>>,
|
||||
port_wmask: Option<Expr<Array<[DynCanonicalValue]>>>,
|
||||
connect_rdata: impl Copy + Fn(&mut Vec<Stmt>, Expr<DynCanonicalValue>, Expr<DynCanonicalValue>),
|
||||
connect_wdata: impl Copy + Fn(&mut Vec<Stmt>, Expr<DynCanonicalValue>, Expr<DynCanonicalValue>),
|
||||
connect_wmask: impl Copy + Fn(&mut Vec<Stmt>, Expr<DynCanonicalValue>, Expr<DynCanonicalValue>),
|
||||
) {
|
||||
let Some((input_array_type, input_array_types_rest)) = input_array_types.split_first()
|
||||
else {
|
||||
|
@ -457,7 +430,8 @@ impl ModuleState {
|
|||
assert_eq!(memory_element_array_range_len % input_array_type.len(), 0);
|
||||
let chunk_size = memory_element_array_range_len / input_array_type.len();
|
||||
for index in 0..input_array_type.len() {
|
||||
let map = |e| Expr::<Array>::from_canonical(e)[index];
|
||||
let map =
|
||||
|e: Expr<DynCanonicalValue>| e.with_type::<Array<[DynCanonicalValue]>>()[index];
|
||||
let wire_rdata = wire_rdata.map(map);
|
||||
let wire_wdata = wire_wdata.map(map);
|
||||
let wire_wmask = wire_wmask.map(map);
|
||||
|
@ -482,20 +456,20 @@ impl ModuleState {
|
|||
fn connect_split_mem_port(
|
||||
&mut self,
|
||||
output_stmts: &mut Vec<Stmt>,
|
||||
mut input_element_type: CanonicalType,
|
||||
mut input_element_type: TypeEnum,
|
||||
single_type: SingleType,
|
||||
source_location: SourceLocation,
|
||||
wire_rdata: Option<Expr<CanonicalType>>,
|
||||
wire_wdata: Option<Expr<CanonicalType>>,
|
||||
wire_wmask: Option<Expr<CanonicalType>>,
|
||||
port_rdata: Option<Expr<CanonicalType>>,
|
||||
port_wdata: Option<Expr<CanonicalType>>,
|
||||
port_wmask: Option<Expr<CanonicalType>>,
|
||||
wire_rdata: Option<Expr<DynCanonicalValue>>,
|
||||
wire_wdata: Option<Expr<DynCanonicalValue>>,
|
||||
wire_wmask: Option<Expr<DynCanonicalValue>>,
|
||||
port_rdata: Option<Expr<DynCanonicalValue>>,
|
||||
port_wdata: Option<Expr<DynCanonicalValue>>,
|
||||
port_wmask: Option<Expr<DynCanonicalValue>>,
|
||||
) {
|
||||
let mut input_array_types = vec![];
|
||||
let connect_read = |output_stmts: &mut Vec<Stmt>,
|
||||
wire_read: Expr<CanonicalType>,
|
||||
port_read: Expr<CanonicalType>| {
|
||||
wire_read: Expr<DynCanonicalValue>,
|
||||
port_read: Expr<DynCanonicalValue>| {
|
||||
output_stmts.push(
|
||||
StmtConnect {
|
||||
lhs: wire_read,
|
||||
|
@ -506,8 +480,8 @@ impl ModuleState {
|
|||
);
|
||||
};
|
||||
let connect_write = |output_stmts: &mut Vec<Stmt>,
|
||||
wire_write: Expr<CanonicalType>,
|
||||
port_write: Expr<CanonicalType>| {
|
||||
wire_write: Expr<DynCanonicalValue>,
|
||||
port_write: Expr<DynCanonicalValue>| {
|
||||
output_stmts.push(
|
||||
StmtConnect {
|
||||
lhs: port_write,
|
||||
|
@ -517,29 +491,32 @@ impl ModuleState {
|
|||
.into(),
|
||||
);
|
||||
};
|
||||
let connect_read_enum = |output_stmts: &mut Vec<Stmt>,
|
||||
wire_read: Expr<CanonicalType>,
|
||||
port_read: Expr<CanonicalType>| {
|
||||
connect_read(
|
||||
output_stmts,
|
||||
wire_read,
|
||||
Expr::<UInt>::from_canonical(port_read).cast_bits_to(Expr::ty(wire_read)),
|
||||
);
|
||||
};
|
||||
let connect_read_enum =
|
||||
|output_stmts: &mut Vec<Stmt>,
|
||||
wire_read: Expr<DynCanonicalValue>,
|
||||
port_read: Expr<DynCanonicalValue>| {
|
||||
connect_read(
|
||||
output_stmts,
|
||||
wire_read,
|
||||
port_read
|
||||
.with_type::<DynUInt>()
|
||||
.cast_bits_to(wire_read.ty()),
|
||||
);
|
||||
};
|
||||
let connect_write_enum =
|
||||
|output_stmts: &mut Vec<Stmt>,
|
||||
wire_write: Expr<CanonicalType>,
|
||||
port_write: Expr<CanonicalType>| {
|
||||
wire_write: Expr<DynCanonicalValue>,
|
||||
port_write: Expr<DynCanonicalValue>| {
|
||||
connect_write(
|
||||
output_stmts,
|
||||
Expr::canonical(wire_write.cast_to_bits()),
|
||||
wire_write.cast_to_bits().to_canonical_dyn(),
|
||||
port_write,
|
||||
);
|
||||
};
|
||||
loop {
|
||||
match input_element_type {
|
||||
CanonicalType::Bundle(_) => unreachable!("bundle types are always split"),
|
||||
CanonicalType::Enum(_)
|
||||
TypeEnum::BundleType(_) => unreachable!("bundle types are always split"),
|
||||
TypeEnum::EnumType(_)
|
||||
if input_array_types
|
||||
.first()
|
||||
.map(|&v| single_type.is_array_type(v))
|
||||
|
@ -555,7 +532,7 @@ impl ModuleState {
|
|||
connect_write(output_stmts, wire_wmask, port_wmask);
|
||||
}
|
||||
}
|
||||
CanonicalType::Enum(_) => Self::connect_split_mem_port_arrays(
|
||||
TypeEnum::EnumType(_) => Self::connect_split_mem_port_arrays(
|
||||
output_stmts,
|
||||
&input_array_types,
|
||||
0,
|
||||
|
@ -563,19 +540,25 @@ impl ModuleState {
|
|||
wire_rdata,
|
||||
wire_wdata,
|
||||
wire_wmask,
|
||||
port_rdata.map(Expr::from_canonical),
|
||||
port_wdata.map(Expr::from_canonical),
|
||||
port_wmask.map(Expr::from_canonical),
|
||||
port_rdata.map(|e: Expr<DynCanonicalValue>| {
|
||||
e.with_type::<Array<[DynCanonicalValue]>>()
|
||||
}),
|
||||
port_wdata.map(|e: Expr<DynCanonicalValue>| {
|
||||
e.with_type::<Array<[DynCanonicalValue]>>()
|
||||
}),
|
||||
port_wmask.map(|e: Expr<DynCanonicalValue>| {
|
||||
e.with_type::<Array<[DynCanonicalValue]>>()
|
||||
}),
|
||||
connect_read_enum,
|
||||
connect_write_enum,
|
||||
connect_write,
|
||||
connect_write_enum,
|
||||
),
|
||||
CanonicalType::Array(array_type) => {
|
||||
TypeEnum::ArrayType(array_type) => {
|
||||
input_array_types.push(array_type);
|
||||
input_element_type = array_type.element();
|
||||
input_element_type = array_type.element().type_enum();
|
||||
continue;
|
||||
}
|
||||
CanonicalType::UInt(_) | CanonicalType::SInt(_) | CanonicalType::Bool(_)
|
||||
TypeEnum::UInt(_) | TypeEnum::SInt(_)
|
||||
if input_array_types
|
||||
.first()
|
||||
.map(|&v| single_type.is_array_type(v))
|
||||
|
@ -591,56 +574,59 @@ impl ModuleState {
|
|||
connect_write(output_stmts, wire_wmask, port_wmask);
|
||||
}
|
||||
}
|
||||
CanonicalType::UInt(_) | CanonicalType::SInt(_) | CanonicalType::Bool(_) => {
|
||||
Self::connect_split_mem_port_arrays(
|
||||
output_stmts,
|
||||
&input_array_types,
|
||||
0,
|
||||
single_type.array_len(),
|
||||
wire_rdata,
|
||||
wire_wdata,
|
||||
wire_wmask,
|
||||
port_rdata.map(Expr::from_canonical),
|
||||
port_wdata.map(Expr::from_canonical),
|
||||
port_wmask.map(Expr::from_canonical),
|
||||
connect_read,
|
||||
connect_write,
|
||||
connect_write,
|
||||
)
|
||||
}
|
||||
CanonicalType::Clock(_)
|
||||
| CanonicalType::AsyncReset(_)
|
||||
| CanonicalType::SyncReset(_)
|
||||
| CanonicalType::Reset(_) => unreachable!("memory element type is a storable type"),
|
||||
TypeEnum::UInt(_) | TypeEnum::SInt(_) => Self::connect_split_mem_port_arrays(
|
||||
output_stmts,
|
||||
&input_array_types,
|
||||
0,
|
||||
single_type.array_len(),
|
||||
wire_rdata,
|
||||
wire_wdata,
|
||||
wire_wmask,
|
||||
port_rdata.map(|e: Expr<DynCanonicalValue>| {
|
||||
e.with_type::<Array<[DynCanonicalValue]>>()
|
||||
}),
|
||||
port_wdata.map(|e: Expr<DynCanonicalValue>| {
|
||||
e.with_type::<Array<[DynCanonicalValue]>>()
|
||||
}),
|
||||
port_wmask.map(|e: Expr<DynCanonicalValue>| {
|
||||
e.with_type::<Array<[DynCanonicalValue]>>()
|
||||
}),
|
||||
connect_read,
|
||||
connect_write,
|
||||
connect_write,
|
||||
),
|
||||
TypeEnum::Clock(_)
|
||||
| TypeEnum::AsyncReset(_)
|
||||
| TypeEnum::SyncReset(_)
|
||||
| TypeEnum::Reset(_) => unreachable!("memory element type is a storable type"),
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
fn create_split_mem(
|
||||
&mut self,
|
||||
input_mem: Mem,
|
||||
input_mem: Mem<[DynCanonicalValue]>,
|
||||
output_stmts: &mut Vec<Stmt>,
|
||||
input_element_type: CanonicalType,
|
||||
input_element_type: TypeEnum,
|
||||
single_type: SingleType,
|
||||
mem_name_path: &str,
|
||||
split_state: &SplitState<'_>,
|
||||
) -> Mem {
|
||||
let mem_name = NameId(
|
||||
Intern::intern_owned(format!("{}{mem_name_path}", input_mem.scoped_name().1 .0)),
|
||||
Id::new(),
|
||||
);
|
||||
) -> Mem<[DynCanonicalValue]> {
|
||||
let mem_name = self.name_id_gen.gen(Intern::intern_owned(format!(
|
||||
"{}{mem_name_path}",
|
||||
input_mem.scoped_name().1 .0
|
||||
)));
|
||||
let mem_name = ScopedNameId(input_mem.scoped_name().0, mem_name);
|
||||
let output_element_type = match single_type {
|
||||
SingleType::UInt(ty) => ty.canonical(),
|
||||
SingleType::SInt(ty) => ty.canonical(),
|
||||
SingleType::Bool(ty) => ty.canonical(),
|
||||
SingleType::UIntArray(ty) => ty.canonical(),
|
||||
SingleType::SIntArray(ty) => ty.canonical(),
|
||||
SingleType::BoolArray(ty) => ty.canonical(),
|
||||
SingleType::UInt(ty) => ty.canonical_dyn(),
|
||||
SingleType::SInt(ty) => ty.canonical_dyn(),
|
||||
SingleType::UIntArray(ty) => ty.canonical_dyn(),
|
||||
SingleType::SIntArray(ty) => ty.canonical_dyn(),
|
||||
};
|
||||
let output_array_type = Array::new_dyn(output_element_type, input_mem.array_type().len());
|
||||
let output_array_type =
|
||||
ArrayType::new_slice(output_element_type, input_mem.array_type().len());
|
||||
let initial_value = split_state.initial_value.as_ref().map(|initial_value| {
|
||||
let mut bits = BitVec::with_capacity(output_array_type.type_properties().bit_width);
|
||||
let mut bits = BitVec::with_capacity(output_array_type.bit_width());
|
||||
for element in initial_value.iter() {
|
||||
bits.extend_from_bitslice(element);
|
||||
}
|
||||
|
@ -657,6 +643,7 @@ impl ModuleState {
|
|||
port.addr_type(),
|
||||
output_element_type,
|
||||
)
|
||||
.intern_sized()
|
||||
})
|
||||
.collect();
|
||||
let output_mem = Mem::new_unchecked(
|
||||
|
@ -689,15 +676,15 @@ impl ModuleState {
|
|||
let port_rdata = port
|
||||
.port_kind()
|
||||
.rdata_name()
|
||||
.map(|name| Expr::field(port_expr, name));
|
||||
.map(|name| port_expr.field(name));
|
||||
let port_wdata = port
|
||||
.port_kind()
|
||||
.wdata_name()
|
||||
.map(|name| Expr::field(port_expr, name));
|
||||
.map(|name| port_expr.field(name));
|
||||
let port_wmask = port
|
||||
.port_kind()
|
||||
.wmask_name()
|
||||
.map(|name| Expr::field(port_expr, name));
|
||||
.map(|name| port_expr.field(name));
|
||||
self.connect_split_mem_port(
|
||||
output_stmts,
|
||||
input_element_type,
|
||||
|
@ -715,11 +702,11 @@ impl ModuleState {
|
|||
}
|
||||
fn process_mem(
|
||||
&mut self,
|
||||
input_mem: Mem,
|
||||
output_mems: &mut Vec<Mem>,
|
||||
input_mem: Mem<[DynCanonicalValue]>,
|
||||
output_mems: &mut Vec<Mem<[DynCanonicalValue]>>,
|
||||
output_stmts: &mut Vec<Stmt>,
|
||||
) {
|
||||
let element_type = input_mem.array_type().element();
|
||||
let element_type = input_mem.array_type().element().type_enum();
|
||||
let mut split = MemSplit::new(element_type);
|
||||
let mem_state = match split {
|
||||
MemSplit::Single {
|
||||
|
@ -752,39 +739,38 @@ impl ModuleState {
|
|||
let port_ty = port.ty();
|
||||
let NameId(mem_name, _) = input_mem.scoped_name().1;
|
||||
let port_name = port.port_name();
|
||||
let wire_name = NameId(
|
||||
Intern::intern_owned(format!("{mem_name}_{port_name}")),
|
||||
Id::new(),
|
||||
);
|
||||
let wire_name = self
|
||||
.name_id_gen
|
||||
.gen(Intern::intern_owned(format!("{mem_name}_{port_name}")));
|
||||
let wire = Wire::new_unchecked(
|
||||
ScopedNameId(input_mem.scoped_name().0, wire_name),
|
||||
port.source_location(),
|
||||
port_ty,
|
||||
);
|
||||
let wire_expr = wire.to_expr();
|
||||
let canonical_wire = wire.canonical();
|
||||
let canonical_wire = wire.to_dyn_canonical_wire();
|
||||
output_stmts.push(
|
||||
StmtWire {
|
||||
annotations: Default::default(),
|
||||
wire: canonical_wire,
|
||||
wire: canonical_wire.clone(),
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
replacement_ports.push(ExprEnum::Wire(canonical_wire));
|
||||
replacement_ports.push(ExprEnum::Wire(canonical_wire.intern_sized()));
|
||||
wire_port_rdata.push(
|
||||
port.port_kind()
|
||||
.rdata_name()
|
||||
.map(|name| Expr::field(wire_expr, name)),
|
||||
.map(|name| wire_expr.field(name)),
|
||||
);
|
||||
wire_port_wdata.push(
|
||||
port.port_kind()
|
||||
.wdata_name()
|
||||
.map(|name| Expr::field(wire_expr, name)),
|
||||
.map(|name| wire_expr.field(name)),
|
||||
);
|
||||
wire_port_wmask.push(
|
||||
port.port_kind()
|
||||
.wmask_name()
|
||||
.map(|name| Expr::field(wire_expr, name)),
|
||||
.map(|name| wire_expr.field(name)),
|
||||
);
|
||||
}
|
||||
let mem_state = MemState {
|
||||
|
@ -829,8 +815,8 @@ impl ModuleState {
|
|||
|
||||
#[derive(Default)]
|
||||
struct State {
|
||||
modules: HashMap<Interned<Module<Bundle>>, ModuleState>,
|
||||
current_module: Option<Interned<Module<Bundle>>>,
|
||||
modules: HashMap<Interned<Module<DynBundle>>, ModuleState>,
|
||||
current_module: Option<Interned<Module<DynBundle>>>,
|
||||
}
|
||||
|
||||
impl State {
|
||||
|
@ -842,11 +828,11 @@ impl State {
|
|||
|
||||
struct PushedState<'a> {
|
||||
state: &'a mut State,
|
||||
old_module: Option<Interned<Module<Bundle>>>,
|
||||
old_module: Option<Interned<Module<DynBundle>>>,
|
||||
}
|
||||
|
||||
impl<'a> PushedState<'a> {
|
||||
fn push_module(state: &'a mut State, module: Interned<Module<Bundle>>) -> Self {
|
||||
fn push_module(state: &'a mut State, module: Interned<Module<DynBundle>>) -> Self {
|
||||
let old_module = state.current_module.replace(module);
|
||||
Self { state, old_module }
|
||||
}
|
||||
|
@ -874,7 +860,10 @@ impl DerefMut for PushedState<'_> {
|
|||
impl Folder for State {
|
||||
type Error = Infallible;
|
||||
|
||||
fn fold_module<T: BundleType>(&mut self, v: Module<T>) -> Result<Module<T>, Self::Error> {
|
||||
fn fold_module<T: BundleValue>(&mut self, v: Module<T>) -> Result<Module<T>, Self::Error>
|
||||
where
|
||||
T::Type: BundleType<Value = T>,
|
||||
{
|
||||
let module: Interned<_> = v.canonical().intern_sized();
|
||||
if let Some(module_state) = self.modules.get(&module) {
|
||||
return Ok(Module::from_canonical(
|
||||
|
@ -887,6 +876,7 @@ impl Folder for State {
|
|||
module,
|
||||
ModuleState {
|
||||
output_module: None,
|
||||
name_id_gen: NameIdGen::for_module(*module),
|
||||
memories: HashMap::new(),
|
||||
},
|
||||
);
|
||||
|
@ -896,10 +886,10 @@ impl Folder for State {
|
|||
Ok(Module::from_canonical(*module))
|
||||
}
|
||||
|
||||
fn fold_mem<Element: Type, Len: Size>(
|
||||
fn fold_mem<VA: ValueArrayOrSlice + ?Sized>(
|
||||
&mut self,
|
||||
_v: Mem<Element, Len>,
|
||||
) -> Result<Mem<Element, Len>, Self::Error> {
|
||||
_v: Mem<VA>,
|
||||
) -> Result<Mem<VA>, Self::Error> {
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
|
@ -943,7 +933,7 @@ impl Folder for State {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn simplify_memories(module: Interned<Module<Bundle>>) -> Interned<Module<Bundle>> {
|
||||
pub fn simplify_memories(module: Interned<Module<DynBundle>>) -> Interned<Module<DynBundle>> {
|
||||
module
|
||||
.fold(&mut State::default())
|
||||
.unwrap_or_else(|v| match v {})
|
||||
|
|
|
@ -2,36 +2,32 @@
|
|||
// See Notices.txt for copyright information
|
||||
#![allow(clippy::multiple_bound_locations)]
|
||||
use crate::{
|
||||
annotations::{
|
||||
Annotation, BlackBoxInlineAnnotation, BlackBoxPathAnnotation, CustomFirrtlAnnotation,
|
||||
DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation, TargetedAnnotation,
|
||||
},
|
||||
array::ArrayType,
|
||||
bundle::{Bundle, BundleField, BundleType},
|
||||
clock::Clock,
|
||||
enum_::{Enum, EnumType, EnumVariant},
|
||||
annotations::{Annotation, CustomFirrtlAnnotation, TargetedAnnotation},
|
||||
array::{Array, ArrayType, ArrayTypeTrait, ValueArrayOrSlice},
|
||||
bundle::{BundleType, BundleValue, DynBundle, DynBundleType, FieldType},
|
||||
clock::{Clock, ClockType},
|
||||
enum_::{DynEnum, DynEnumType, EnumType, EnumValue, VariantType},
|
||||
expr::{
|
||||
ops,
|
||||
target::{
|
||||
Target, TargetBase, TargetChild, TargetPathArrayElement, TargetPathBundleField,
|
||||
TargetPathDynArrayElement, TargetPathElement,
|
||||
},
|
||||
Expr, ExprEnum,
|
||||
ops, Expr, ExprEnum, Literal, Target, TargetBase, TargetChild, TargetPathArrayElement,
|
||||
TargetPathBundleField, TargetPathDynArrayElement, TargetPathElement, ToExpr,
|
||||
},
|
||||
int::{
|
||||
DynInt, DynIntType, DynSInt, DynSIntType, DynUInt, DynUIntType, StaticOrDynIntType, IntType,
|
||||
IntTypeTrait,
|
||||
},
|
||||
formal::FormalKind,
|
||||
int::{Bool, SIntType, SIntValue, Size, UIntType, UIntValue},
|
||||
intern::{Intern, Interned},
|
||||
memory::{Mem, MemPort, PortKind, PortName, PortType, ReadUnderWrite},
|
||||
module::{
|
||||
AnnotatedModuleIO, Block, BlockId, ExternModuleBody, ExternModuleParameter,
|
||||
ExternModuleParameterValue, Instance, Module, ModuleBody, ModuleIO, NameId,
|
||||
NormalModuleBody, ScopedNameId, Stmt, StmtConnect, StmtDeclaration, StmtFormal, StmtIf,
|
||||
StmtInstance, StmtMatch, StmtReg, StmtWire,
|
||||
NormalModuleBody, ScopedNameId, Stmt, StmtConnect, StmtDeclaration, StmtIf, StmtInstance,
|
||||
StmtMatch, StmtReg, StmtWire,
|
||||
},
|
||||
reg::Reg,
|
||||
reset::{AsyncReset, Reset, SyncReset},
|
||||
reset::{AsyncReset, AsyncResetType, Reset, ResetType, SyncReset, SyncResetType},
|
||||
source_location::SourceLocation,
|
||||
ty::{CanonicalType, Type},
|
||||
ty::{DynCanonicalType, DynCanonicalValue, DynType, Type, TypeEnum, Value, ValueEnum},
|
||||
util::{ConstBool, GenericConstBool},
|
||||
wire::Wire,
|
||||
};
|
||||
use num_bigint::{BigInt, BigUint};
|
||||
|
@ -477,4 +473,7 @@ impl<T: ?Sized + Visit<State>, State: ?Sized + Visitor> Visit<State> for &'_ mut
|
|||
}
|
||||
}
|
||||
|
||||
type InternedDynType = Interned<dyn DynType>;
|
||||
type InternedDynCanonicalType = Interned<dyn DynCanonicalType>;
|
||||
|
||||
include!(concat!(env!("OUT_DIR"), "/visit.rs"));
|
||||
|
|
|
@ -1,36 +0,0 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
pub use crate::{
|
||||
annotations::{
|
||||
BlackBoxInlineAnnotation, BlackBoxPathAnnotation, CustomFirrtlAnnotation,
|
||||
DocStringAnnotation, DontTouchAnnotation, SVAttributeAnnotation,
|
||||
},
|
||||
array::{Array, ArrayType},
|
||||
bundle::Bundle,
|
||||
cli::Cli,
|
||||
clock::{Clock, ClockDomain, ToClock},
|
||||
enum_::{Enum, HdlNone, HdlOption, HdlSome},
|
||||
expr::{
|
||||
repeat, CastBitsTo, CastTo, CastToBits, Expr, HdlPartialEq, HdlPartialOrd, MakeUninitExpr,
|
||||
ReduceBits, ToExpr,
|
||||
},
|
||||
formal::{
|
||||
all_const, all_seq, any_const, any_seq, formal_global_clock, formal_reset, hdl_assert,
|
||||
hdl_assert_with_enable, hdl_assume, hdl_assume_with_enable, hdl_cover,
|
||||
hdl_cover_with_enable, MakeFormalExpr,
|
||||
},
|
||||
hdl, hdl_module,
|
||||
int::{Bool, DynSize, KnownSize, SInt, SIntType, Size, UInt, UIntType},
|
||||
memory::{Mem, MemBuilder, ReadUnderWrite},
|
||||
module::{
|
||||
annotate, connect, connect_any, incomplete_wire, instance, memory, memory_array,
|
||||
memory_with_init, reg_builder, wire, Instance, Module, ModuleBuilder,
|
||||
},
|
||||
reg::Reg,
|
||||
reset::{AsyncReset, Reset, SyncReset, ToAsyncReset, ToReset, ToSyncReset},
|
||||
source_location::SourceLocation,
|
||||
ty::{AsMask, CanonicalType, Type},
|
||||
util::{ConstUsize, GenericConstUsize},
|
||||
wire::Wire,
|
||||
__,
|
||||
};
|
|
@ -2,21 +2,21 @@
|
|||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
clock::ClockDomain,
|
||||
expr::{Expr, Flow},
|
||||
expr::{Expr, ExprTrait, Flow, ToExpr},
|
||||
intern::Interned,
|
||||
module::{NameId, ScopedNameId},
|
||||
source_location::SourceLocation,
|
||||
ty::{CanonicalType, Type},
|
||||
ty::{DynCanonicalType, DynType, Type},
|
||||
};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
||||
#[derive(Clone, Eq, PartialEq, Hash)]
|
||||
pub struct Reg<T: Type> {
|
||||
name: ScopedNameId,
|
||||
source_location: SourceLocation,
|
||||
ty: T,
|
||||
clock_domain: Expr<ClockDomain>,
|
||||
init: Option<Expr<T>>,
|
||||
init: Option<Expr<T::Value>>,
|
||||
}
|
||||
|
||||
impl<T: Type + fmt::Debug> fmt::Debug for Reg<T> {
|
||||
|
@ -37,8 +37,20 @@ impl<T: Type + fmt::Debug> fmt::Debug for Reg<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Type> ToExpr for Reg<T> {
|
||||
type Type = T;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
self.ty.clone()
|
||||
}
|
||||
|
||||
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
|
||||
Expr::new_unchecked(self.expr_enum())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type> Reg<T> {
|
||||
pub fn canonical(&self) -> Reg<CanonicalType> {
|
||||
pub fn canonical(&self) -> Reg<T::CanonicalType> {
|
||||
let Self {
|
||||
name,
|
||||
source_location,
|
||||
|
@ -54,20 +66,49 @@ impl<T: Type> Reg<T> {
|
|||
init: init.map(Expr::canonical),
|
||||
}
|
||||
}
|
||||
pub fn to_dyn_reg(&self) -> Reg<Interned<dyn DynType>> {
|
||||
let Self {
|
||||
name,
|
||||
source_location,
|
||||
ref ty,
|
||||
clock_domain,
|
||||
init,
|
||||
} = *self;
|
||||
Reg {
|
||||
name,
|
||||
source_location,
|
||||
ty: ty.to_dyn(),
|
||||
clock_domain,
|
||||
init: init.map(Expr::to_dyn),
|
||||
}
|
||||
}
|
||||
pub fn to_dyn_canonical_reg(&self) -> Reg<Interned<dyn DynCanonicalType>> {
|
||||
let Self {
|
||||
name,
|
||||
source_location,
|
||||
ref ty,
|
||||
clock_domain,
|
||||
init,
|
||||
} = *self;
|
||||
Reg {
|
||||
name,
|
||||
source_location,
|
||||
ty: ty.canonical_dyn(),
|
||||
clock_domain,
|
||||
init: init.map(Expr::to_canonical_dyn),
|
||||
}
|
||||
}
|
||||
#[track_caller]
|
||||
pub fn new_unchecked(
|
||||
scoped_name: ScopedNameId,
|
||||
source_location: SourceLocation,
|
||||
ty: T,
|
||||
clock_domain: Expr<ClockDomain>,
|
||||
init: Option<Expr<T>>,
|
||||
init: Option<Expr<T::Value>>,
|
||||
) -> Self {
|
||||
assert!(
|
||||
ty.canonical().is_storable(),
|
||||
"register type must be a storable type"
|
||||
);
|
||||
assert!(ty.is_storable(), "register type must be a storable type");
|
||||
if let Some(init) = init {
|
||||
assert_eq!(ty, Expr::ty(init), "register's type must match init type");
|
||||
assert_eq!(ty, init.ty(), "register's type must match init type");
|
||||
}
|
||||
Self {
|
||||
name: scoped_name,
|
||||
|
@ -77,9 +118,6 @@ impl<T: Type> Reg<T> {
|
|||
init,
|
||||
}
|
||||
}
|
||||
pub fn ty(&self) -> T {
|
||||
self.ty
|
||||
}
|
||||
pub fn source_location(&self) -> SourceLocation {
|
||||
self.source_location
|
||||
}
|
||||
|
@ -101,7 +139,7 @@ impl<T: Type> Reg<T> {
|
|||
pub fn clock_domain(&self) -> Expr<ClockDomain> {
|
||||
self.clock_domain
|
||||
}
|
||||
pub fn init(&self) -> Option<Expr<T>> {
|
||||
pub fn init(&self) -> Option<Expr<T::Value>> {
|
||||
self.init
|
||||
}
|
||||
pub fn flow(&self) -> Flow {
|
||||
|
|
|
@ -2,119 +2,358 @@
|
|||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
expr::{Expr, ToExpr},
|
||||
int::Bool,
|
||||
int::{UInt, UIntType},
|
||||
intern::Interned,
|
||||
source_location::SourceLocation,
|
||||
ty::{impl_match_variant_as_self, CanonicalType, StaticType, Type, TypeProperties},
|
||||
ty::{
|
||||
impl_match_values_as_self, CanonicalType, CanonicalTypeKind, CanonicalValue, Connect,
|
||||
DynCanonicalType, StaticType, Type, TypeEnum, Value, ValueEnum,
|
||||
},
|
||||
util::interned_bit,
|
||||
};
|
||||
use bitvec::slice::BitSlice;
|
||||
|
||||
mod sealed {
|
||||
pub trait ResetTypeSealed {}
|
||||
pub trait ResetTypeTrait: CanonicalType + StaticType<MaskType = UIntType<1>> {}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
|
||||
pub struct AsyncResetType;
|
||||
|
||||
impl AsyncResetType {
|
||||
pub const fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ResetType: StaticType<MaskType = Bool> + sealed::ResetTypeSealed {}
|
||||
impl Type for AsyncResetType {
|
||||
type Value = AsyncReset;
|
||||
type CanonicalType = AsyncResetType;
|
||||
type CanonicalValue = AsyncReset;
|
||||
type MaskType = UIntType<1>;
|
||||
type MaskValue = UInt<1>;
|
||||
|
||||
macro_rules! reset_type {
|
||||
($name:ident, $Trait:ident::$trait_fn:ident, $is_castable_from_bits:literal) => {
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
|
||||
pub struct $name;
|
||||
impl_match_values_as_self!();
|
||||
|
||||
impl Type for $name {
|
||||
type BaseType = $name;
|
||||
type MaskType = Bool;
|
||||
fn mask_type(&self) -> Self::MaskType {
|
||||
UIntType::new()
|
||||
}
|
||||
|
||||
impl_match_variant_as_self!();
|
||||
fn canonical(&self) -> Self::CanonicalType {
|
||||
*self
|
||||
}
|
||||
|
||||
fn mask_type(&self) -> Self::MaskType {
|
||||
Bool
|
||||
}
|
||||
fn source_location(&self) -> SourceLocation {
|
||||
SourceLocation::builtin()
|
||||
}
|
||||
|
||||
fn canonical(&self) -> CanonicalType {
|
||||
CanonicalType::$name(*self)
|
||||
}
|
||||
fn type_enum(&self) -> TypeEnum {
|
||||
TypeEnum::AsyncReset(*self)
|
||||
}
|
||||
|
||||
fn source_location() -> SourceLocation {
|
||||
SourceLocation::builtin()
|
||||
}
|
||||
fn from_canonical_type(t: Self::CanonicalType) -> Self {
|
||||
t
|
||||
}
|
||||
|
||||
fn from_canonical(canonical_type: CanonicalType) -> Self {
|
||||
let CanonicalType::$name(retval) = canonical_type else {
|
||||
panic!("expected {}", stringify!($name));
|
||||
};
|
||||
retval
|
||||
}
|
||||
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
|
||||
Some(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl Connect<Self> for AsyncResetType {}
|
||||
|
||||
impl CanonicalType for AsyncResetType {
|
||||
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::AsyncReset;
|
||||
}
|
||||
|
||||
impl StaticType for AsyncResetType {
|
||||
fn static_type() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl ResetTypeTrait for AsyncResetType {}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct AsyncReset(pub bool);
|
||||
|
||||
impl ToExpr for AsyncReset {
|
||||
type Type = AsyncResetType;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
AsyncResetType
|
||||
}
|
||||
|
||||
fn to_expr(&self) -> Expr<Self> {
|
||||
Expr::from_value(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Value for AsyncReset {
|
||||
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
|
||||
*self
|
||||
}
|
||||
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
|
||||
interned_bit(this.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl CanonicalValue for AsyncReset {
|
||||
fn value_enum_impl(this: &Self) -> ValueEnum {
|
||||
ValueEnum::AsyncReset(*this)
|
||||
}
|
||||
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
|
||||
interned_bit(this.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
|
||||
pub struct SyncResetType;
|
||||
|
||||
impl SyncResetType {
|
||||
pub const fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl Type for SyncResetType {
|
||||
type CanonicalType = SyncResetType;
|
||||
type Value = SyncReset;
|
||||
type CanonicalValue = SyncReset;
|
||||
type MaskType = UIntType<1>;
|
||||
type MaskValue = UInt<1>;
|
||||
|
||||
impl_match_values_as_self!();
|
||||
|
||||
fn mask_type(&self) -> Self::MaskType {
|
||||
UIntType::new()
|
||||
}
|
||||
|
||||
fn canonical(&self) -> Self::CanonicalType {
|
||||
*self
|
||||
}
|
||||
|
||||
fn source_location(&self) -> SourceLocation {
|
||||
SourceLocation::builtin()
|
||||
}
|
||||
|
||||
fn type_enum(&self) -> TypeEnum {
|
||||
TypeEnum::SyncReset(*self)
|
||||
}
|
||||
|
||||
fn from_canonical_type(t: Self::CanonicalType) -> Self {
|
||||
t
|
||||
}
|
||||
|
||||
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
|
||||
Some(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl Connect<Self> for SyncResetType {}
|
||||
|
||||
impl CanonicalType for SyncResetType {
|
||||
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::SyncReset;
|
||||
}
|
||||
|
||||
impl StaticType for SyncResetType {
|
||||
fn static_type() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl ResetTypeTrait for SyncResetType {}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, Default)]
|
||||
pub struct SyncReset(pub bool);
|
||||
|
||||
impl ToExpr for SyncReset {
|
||||
type Type = SyncResetType;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
SyncResetType
|
||||
}
|
||||
|
||||
fn to_expr(&self) -> Expr<Self> {
|
||||
Expr::from_value(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Value for SyncReset {
|
||||
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
|
||||
*self
|
||||
}
|
||||
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
|
||||
interned_bit(this.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl CanonicalValue for SyncReset {
|
||||
fn value_enum_impl(this: &Self) -> ValueEnum {
|
||||
ValueEnum::SyncReset(*this)
|
||||
}
|
||||
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
|
||||
interned_bit(this.0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Default)]
|
||||
pub struct ResetType;
|
||||
|
||||
impl ResetType {
|
||||
pub const fn new() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl Type for ResetType {
|
||||
type Value = Reset;
|
||||
type CanonicalType = ResetType;
|
||||
type CanonicalValue = Reset;
|
||||
type MaskType = UIntType<1>;
|
||||
type MaskValue = UInt<1>;
|
||||
|
||||
impl_match_values_as_self!();
|
||||
|
||||
fn mask_type(&self) -> Self::MaskType {
|
||||
UIntType::new()
|
||||
}
|
||||
|
||||
fn canonical(&self) -> Self::CanonicalType {
|
||||
*self
|
||||
}
|
||||
|
||||
fn source_location(&self) -> SourceLocation {
|
||||
SourceLocation::builtin()
|
||||
}
|
||||
|
||||
fn type_enum(&self) -> TypeEnum {
|
||||
TypeEnum::Reset(*self)
|
||||
}
|
||||
|
||||
fn from_canonical_type(t: Self::CanonicalType) -> Self {
|
||||
t
|
||||
}
|
||||
|
||||
fn as_dyn_canonical_type_impl(this: &Self) -> Option<&dyn DynCanonicalType> {
|
||||
Some(this)
|
||||
}
|
||||
}
|
||||
|
||||
impl Connect<Self> for ResetType {}
|
||||
|
||||
impl CanonicalType for ResetType {
|
||||
const CANONICAL_TYPE_KIND: CanonicalTypeKind = CanonicalTypeKind::Reset;
|
||||
}
|
||||
|
||||
impl StaticType for ResetType {
|
||||
fn static_type() -> Self {
|
||||
Self
|
||||
}
|
||||
}
|
||||
|
||||
impl ResetTypeTrait for ResetType {}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
pub enum Reset {}
|
||||
|
||||
impl ToExpr for Reset {
|
||||
type Type = ResetType;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
match *self {}
|
||||
}
|
||||
|
||||
fn to_expr(&self) -> Expr<Self> {
|
||||
Expr::from_value(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl Value for Reset {
|
||||
fn to_canonical(&self) -> <Self::Type as Type>::CanonicalValue {
|
||||
*self
|
||||
}
|
||||
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
|
||||
match *this {}
|
||||
}
|
||||
}
|
||||
|
||||
impl CanonicalValue for Reset {
|
||||
fn value_enum_impl(this: &Self) -> ValueEnum {
|
||||
ValueEnum::Reset(*this)
|
||||
}
|
||||
fn to_bits_impl(this: &Self) -> Interned<BitSlice> {
|
||||
match *this {}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! make_to_reset {
|
||||
(
|
||||
$(#[from_value($from_value_ty:ty)])*
|
||||
$vis:vis trait $Trait:ident {
|
||||
fn $fn:ident(&self) -> Expr<$T:ty>;
|
||||
}
|
||||
|
||||
impl $name {
|
||||
pub fn type_properties(self) -> TypeProperties {
|
||||
Self::TYPE_PROPERTIES
|
||||
}
|
||||
pub fn can_connect(self, _rhs: Self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl StaticType for $name {
|
||||
const TYPE: Self = Self;
|
||||
const MASK_TYPE: Self::MaskType = Bool;
|
||||
const TYPE_PROPERTIES: TypeProperties = TypeProperties {
|
||||
is_passive: true,
|
||||
is_storable: false,
|
||||
is_castable_from_bits: $is_castable_from_bits,
|
||||
bit_width: 1,
|
||||
};
|
||||
const MASK_TYPE_PROPERTIES: TypeProperties = Bool::TYPE_PROPERTIES;
|
||||
}
|
||||
|
||||
impl sealed::ResetTypeSealed for $name {}
|
||||
|
||||
impl ResetType for $name {}
|
||||
|
||||
pub trait $Trait {
|
||||
fn $trait_fn(&self) -> Expr<$name>;
|
||||
) => {
|
||||
$vis trait $Trait {
|
||||
fn $fn(&self) -> Expr<$T>;
|
||||
}
|
||||
|
||||
impl<T: ?Sized + $Trait> $Trait for &'_ T {
|
||||
fn $trait_fn(&self) -> Expr<$name> {
|
||||
(**self).$trait_fn()
|
||||
fn $fn(&self) -> Expr<$T> {
|
||||
(**self).$fn()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + $Trait> $Trait for &'_ mut T {
|
||||
fn $trait_fn(&self) -> Expr<$name> {
|
||||
(**self).$trait_fn()
|
||||
fn $fn(&self) -> Expr<$T> {
|
||||
(**self).$fn()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized + $Trait> $Trait for Box<T> {
|
||||
fn $trait_fn(&self) -> Expr<$name> {
|
||||
(**self).$trait_fn()
|
||||
fn $fn(&self) -> Expr<$T> {
|
||||
(**self).$fn()
|
||||
}
|
||||
}
|
||||
|
||||
impl $Trait for Expr<$name> {
|
||||
fn $trait_fn(&self) -> Expr<$name> {
|
||||
impl $Trait for Expr<$T> {
|
||||
fn $fn(&self) -> Expr<$T> {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
impl $Trait for $T {
|
||||
fn $fn(&self) -> Expr<$T> {
|
||||
self.to_expr()
|
||||
}
|
||||
}
|
||||
|
||||
$(impl $Trait for $from_value_ty {
|
||||
fn $fn(&self) -> Expr<$T> {
|
||||
self.to_expr().$fn()
|
||||
}
|
||||
})*
|
||||
};
|
||||
}
|
||||
|
||||
reset_type!(AsyncReset, ToAsyncReset::to_async_reset, true);
|
||||
reset_type!(SyncReset, ToSyncReset::to_sync_reset, true);
|
||||
reset_type!(
|
||||
Reset,
|
||||
ToReset::to_reset,
|
||||
false // Reset is not castable from bits because we don't know if it's async or sync
|
||||
);
|
||||
|
||||
impl ToSyncReset for bool {
|
||||
fn to_sync_reset(&self) -> Expr<SyncReset> {
|
||||
self.to_expr().to_sync_reset()
|
||||
make_to_reset! {
|
||||
#[from_value(SyncReset)]
|
||||
#[from_value(AsyncReset)]
|
||||
pub trait ToReset {
|
||||
fn to_reset(&self) -> Expr<Reset>;
|
||||
}
|
||||
}
|
||||
|
||||
impl ToAsyncReset for bool {
|
||||
fn to_async_reset(&self) -> Expr<AsyncReset> {
|
||||
self.to_expr().to_async_reset()
|
||||
make_to_reset! {
|
||||
#[from_value(bool)]
|
||||
#[from_value(UInt<1>)]
|
||||
pub trait ToAsyncReset {
|
||||
fn to_async_reset(&self) -> Expr<AsyncReset>;
|
||||
}
|
||||
}
|
||||
|
||||
make_to_reset! {
|
||||
#[from_value(bool)]
|
||||
#[from_value(UInt<1>)]
|
||||
pub trait ToSyncReset {
|
||||
fn to_sync_reset(&self) -> Expr<SyncReset>;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,122 +0,0 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
cli::{FormalArgs, FormalMode, FormalOutput, RunPhase},
|
||||
firrtl::ExportOptions,
|
||||
};
|
||||
use clap::Parser;
|
||||
use hashbrown::HashMap;
|
||||
use serde::Deserialize;
|
||||
use std::{
|
||||
fmt::Write,
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
sync::{Mutex, OnceLock},
|
||||
};
|
||||
|
||||
fn assert_formal_helper() -> FormalArgs {
|
||||
static FORMAL_ARGS: OnceLock<FormalArgs> = OnceLock::new();
|
||||
// ensure we only run parsing once, so errors from env vars don't produce overlapping output if we're called on multiple threads
|
||||
FORMAL_ARGS
|
||||
.get_or_init(|| FormalArgs::parse_from(["fayalite::testing::assert_formal"]))
|
||||
.clone()
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct CargoMetadata {
|
||||
target_directory: String,
|
||||
}
|
||||
|
||||
fn get_cargo_target_dir() -> &'static Path {
|
||||
static RETVAL: OnceLock<PathBuf> = OnceLock::new();
|
||||
RETVAL.get_or_init(|| {
|
||||
let output = Command::new(
|
||||
std::env::var_os("CARGO")
|
||||
.as_deref()
|
||||
.unwrap_or("cargo".as_ref()),
|
||||
)
|
||||
.arg("metadata")
|
||||
.output()
|
||||
.expect("can't run `cargo metadata`");
|
||||
if !output.status.success() {
|
||||
panic!(
|
||||
"can't run `cargo metadata`:\n{}\nexited with status: {}",
|
||||
String::from_utf8_lossy(&output.stderr),
|
||||
output.status
|
||||
);
|
||||
}
|
||||
let CargoMetadata { target_directory } =
|
||||
serde_json::from_slice(&output.stdout).expect("can't parse output of `cargo metadata`");
|
||||
PathBuf::from(target_directory)
|
||||
})
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn get_assert_formal_target_path(test_name: &dyn std::fmt::Display) -> PathBuf {
|
||||
static DIRS: Mutex<Option<HashMap<String, u64>>> = Mutex::new(None);
|
||||
let test_name = test_name.to_string();
|
||||
// don't use line/column numbers since that constantly changes as you edit tests
|
||||
let file = std::panic::Location::caller().file();
|
||||
// simple reproducible hash
|
||||
let simple_hash = file.bytes().chain(test_name.bytes()).fold(
|
||||
((file.len() as u32) << 16).wrapping_add(test_name.len() as u32),
|
||||
|mut h, b| {
|
||||
h = h.wrapping_mul(0xaa0d184b);
|
||||
h ^= h.rotate_right(5);
|
||||
h ^= h.rotate_right(13);
|
||||
h.wrapping_add(b as u32)
|
||||
},
|
||||
);
|
||||
let mut dir = String::with_capacity(64);
|
||||
for ch in Path::new(file)
|
||||
.file_stem()
|
||||
.unwrap_or_default()
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.chars()
|
||||
.chain(['-'])
|
||||
.chain(test_name.chars())
|
||||
{
|
||||
dir.push(match ch {
|
||||
ch if ch.is_alphanumeric() => ch,
|
||||
'_' | '-' | '+' | '.' | ',' | ' ' => ch,
|
||||
_ => '_',
|
||||
});
|
||||
}
|
||||
write!(dir, "-{simple_hash:08x}").unwrap();
|
||||
let index = *DIRS
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get_or_insert_with(HashMap::new)
|
||||
.entry_ref(&dir)
|
||||
.and_modify(|v| *v += 1)
|
||||
.or_insert(0);
|
||||
write!(dir, "-{index}").unwrap();
|
||||
get_cargo_target_dir()
|
||||
.join("fayalite_assert_formal")
|
||||
.join(dir)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
pub fn assert_formal<M>(
|
||||
test_name: impl std::fmt::Display,
|
||||
module: M,
|
||||
mode: FormalMode,
|
||||
depth: u64,
|
||||
solver: Option<&str>,
|
||||
export_options: ExportOptions,
|
||||
) where
|
||||
FormalArgs: RunPhase<M, Output = FormalOutput>,
|
||||
{
|
||||
let mut args = assert_formal_helper();
|
||||
args.verilog.firrtl.base.redirect_output_for_rust_test = true;
|
||||
args.verilog.firrtl.base.output = Some(get_assert_formal_target_path(&test_name));
|
||||
args.verilog.firrtl.export_options = export_options;
|
||||
args.verilog.debug = true;
|
||||
args.mode = mode;
|
||||
args.depth = depth;
|
||||
if let Some(solver) = solver {
|
||||
args.solver = solver.into();
|
||||
}
|
||||
args.run(module).expect("testing::assert_formal() failed");
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -3,15 +3,10 @@
|
|||
|
||||
mod const_bool;
|
||||
mod const_cmp;
|
||||
mod const_usize;
|
||||
mod misc;
|
||||
mod scoped_ref;
|
||||
pub(crate) mod streaming_read_utf8;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use const_bool::{ConstBool, ConstBoolDispatch, ConstBoolDispatchTag, GenericConstBool};
|
||||
#[doc(inline)]
|
||||
pub use const_usize::{ConstUsize, GenericConstUsize};
|
||||
|
||||
#[doc(inline)]
|
||||
pub use const_cmp::{
|
||||
|
@ -19,13 +14,7 @@ pub use const_cmp::{
|
|||
const_usize_cmp,
|
||||
};
|
||||
|
||||
#[doc(inline)]
|
||||
pub use scoped_ref::ScopedRef;
|
||||
|
||||
#[doc(inline)]
|
||||
pub use misc::{
|
||||
interned_bit, iter_eq_by, BitSliceWriteWithBase, DebugAsDisplay, DebugAsRawString, MakeMutSlice,
|
||||
};
|
||||
|
||||
pub mod job_server;
|
||||
pub mod ready_valid;
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use std::{fmt::Debug, hash::Hash};
|
||||
|
||||
mod sealed {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
|
||||
/// the only implementation is `ConstUsize<Self::VALUE>`
|
||||
pub trait GenericConstUsize:
|
||||
sealed::Sealed + Copy + Ord + Hash + Default + Debug + 'static + Send + Sync
|
||||
{
|
||||
const VALUE: usize;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
pub struct ConstUsize<const VALUE: usize>;
|
||||
|
||||
impl<const VALUE: usize> Debug for ConstUsize<VALUE> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_tuple("ConstUsize").field(&Self::VALUE).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<const VALUE: usize> sealed::Sealed for ConstUsize<VALUE> {}
|
||||
|
||||
impl<const VALUE: usize> GenericConstUsize for ConstUsize<VALUE> {
|
||||
const VALUE: usize = VALUE;
|
||||
}
|
|
@ -1,193 +0,0 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
|
||||
use ctor::ctor;
|
||||
use jobslot::{Acquired, Client};
|
||||
use std::{
|
||||
ffi::OsString,
|
||||
mem,
|
||||
num::NonZeroUsize,
|
||||
sync::{Condvar, Mutex, Once, OnceLock},
|
||||
thread::spawn,
|
||||
};
|
||||
|
||||
fn get_or_make_client() -> &'static Client {
|
||||
#[ctor]
|
||||
static CLIENT: OnceLock<Client> = unsafe {
|
||||
match Client::from_env() {
|
||||
Some(client) => OnceLock::from(client),
|
||||
None => OnceLock::new(),
|
||||
}
|
||||
};
|
||||
|
||||
CLIENT.get_or_init(|| {
|
||||
let mut available_parallelism = None;
|
||||
let mut args = std::env::args_os().skip(1);
|
||||
while let Some(arg) = args.next() {
|
||||
const TEST_THREADS_OPTION: &'static [u8] = b"--test-threads";
|
||||
if arg.as_encoded_bytes().starts_with(TEST_THREADS_OPTION) {
|
||||
match arg.as_encoded_bytes().get(TEST_THREADS_OPTION.len()) {
|
||||
Some(b'=') => {
|
||||
let mut arg = arg.into_encoded_bytes();
|
||||
arg.drain(..=TEST_THREADS_OPTION.len());
|
||||
available_parallelism = Some(arg);
|
||||
break;
|
||||
}
|
||||
None => {
|
||||
available_parallelism = args.next().map(OsString::into_encoded_bytes);
|
||||
break;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
let available_parallelism = if let Some(available_parallelism) = available_parallelism
|
||||
.as_deref()
|
||||
.and_then(|v| std::str::from_utf8(v).ok())
|
||||
.and_then(|v| v.parse().ok())
|
||||
{
|
||||
available_parallelism
|
||||
} else if let Ok(available_parallelism) = std::thread::available_parallelism() {
|
||||
available_parallelism
|
||||
} else {
|
||||
NonZeroUsize::new(1).unwrap()
|
||||
};
|
||||
Client::new_with_fifo(available_parallelism.get() - 1).expect("failed to create job server")
|
||||
})
|
||||
}
|
||||
|
||||
struct State {
|
||||
waiting_count: usize,
|
||||
available: Vec<Acquired>,
|
||||
implicit_available: bool,
|
||||
}
|
||||
|
||||
impl State {
|
||||
fn total_available(&self) -> usize {
|
||||
self.available.len() + self.implicit_available as usize
|
||||
}
|
||||
fn additional_waiting(&self) -> usize {
|
||||
self.waiting_count.saturating_sub(self.total_available())
|
||||
}
|
||||
}
|
||||
|
||||
static STATE: Mutex<State> = Mutex::new(State {
|
||||
waiting_count: 0,
|
||||
available: Vec::new(),
|
||||
implicit_available: true,
|
||||
});
|
||||
static COND_VAR: Condvar = Condvar::new();
|
||||
|
||||
#[derive(Debug)]
|
||||
enum AcquiredJobInner {
|
||||
FromJobServer(Acquired),
|
||||
ImplicitJob,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct AcquiredJob {
|
||||
job: AcquiredJobInner,
|
||||
}
|
||||
|
||||
impl AcquiredJob {
|
||||
fn start_acquire_thread() {
|
||||
static STARTED_THREAD: Once = Once::new();
|
||||
STARTED_THREAD.call_once(|| {
|
||||
spawn(|| {
|
||||
let mut acquired = None;
|
||||
let client = get_or_make_client();
|
||||
let mut state = STATE.lock().unwrap();
|
||||
loop {
|
||||
state = if state.additional_waiting() == 0 {
|
||||
if acquired.is_some() {
|
||||
drop(state);
|
||||
drop(acquired.take()); // drop Acquired outside of lock
|
||||
STATE.lock().unwrap()
|
||||
} else {
|
||||
COND_VAR.wait(state).unwrap()
|
||||
}
|
||||
} else if acquired.is_some() {
|
||||
// allocate space before moving Acquired to ensure we
|
||||
// drop Acquired outside of the lock on panic
|
||||
state.available.reserve(1);
|
||||
state.available.push(acquired.take().unwrap());
|
||||
COND_VAR.notify_all();
|
||||
state
|
||||
} else {
|
||||
drop(state);
|
||||
acquired = Some(
|
||||
client
|
||||
.acquire()
|
||||
.expect("can't acquire token from job server"),
|
||||
);
|
||||
STATE.lock().unwrap()
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
fn acquire_inner(block: bool) -> Option<Self> {
|
||||
Self::start_acquire_thread();
|
||||
let mut state = STATE.lock().unwrap();
|
||||
loop {
|
||||
if let Some(acquired) = state.available.pop() {
|
||||
return Some(Self {
|
||||
job: AcquiredJobInner::FromJobServer(acquired),
|
||||
});
|
||||
}
|
||||
if state.implicit_available {
|
||||
state.implicit_available = false;
|
||||
return Some(Self {
|
||||
job: AcquiredJobInner::ImplicitJob,
|
||||
});
|
||||
}
|
||||
if !block {
|
||||
return None;
|
||||
}
|
||||
state.waiting_count += 1;
|
||||
state = COND_VAR.wait(state).unwrap();
|
||||
state.waiting_count -= 1;
|
||||
}
|
||||
}
|
||||
pub fn try_acquire() -> Option<Self> {
|
||||
Self::acquire_inner(false)
|
||||
}
|
||||
pub fn acquire() -> Self {
|
||||
Self::acquire_inner(true).expect("failed to acquire token")
|
||||
}
|
||||
pub fn run_command<R>(
|
||||
&mut self,
|
||||
cmd: std::process::Command,
|
||||
f: impl FnOnce(&mut std::process::Command) -> std::io::Result<R>,
|
||||
) -> std::io::Result<R> {
|
||||
get_or_make_client().configure_make_and_run_with_fifo(cmd, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for AcquiredJob {
|
||||
fn drop(&mut self) {
|
||||
let mut state = STATE.lock().unwrap();
|
||||
match &self.job {
|
||||
AcquiredJobInner::FromJobServer(_) => {
|
||||
if state.waiting_count > state.available.len() + state.implicit_available as usize {
|
||||
// allocate space before moving Acquired to ensure we
|
||||
// drop Acquired outside of the lock on panic
|
||||
state.available.reserve(1);
|
||||
let AcquiredJobInner::FromJobServer(acquired) =
|
||||
mem::replace(&mut self.job, AcquiredJobInner::ImplicitJob)
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
state.available.push(acquired);
|
||||
COND_VAR.notify_all();
|
||||
}
|
||||
}
|
||||
AcquiredJobInner::ImplicitJob => {
|
||||
state.implicit_available = true;
|
||||
if state.waiting_count > state.available.len() {
|
||||
COND_VAR.notify_all();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,433 +0,0 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use crate::{memory::splat_mask, prelude::*};
|
||||
use std::num::NonZeroUsize;
|
||||
|
||||
#[hdl]
|
||||
pub struct ReadyValid<T> {
|
||||
pub data: HdlOption<T>,
|
||||
#[hdl(flip)]
|
||||
pub ready: Bool,
|
||||
}
|
||||
|
||||
impl<T: Type> ReadyValid<T> {
|
||||
#[hdl]
|
||||
pub fn firing(expr: Expr<Self>) -> Expr<Bool> {
|
||||
#[hdl]
|
||||
let firing: Bool = wire();
|
||||
#[hdl]
|
||||
match expr.data {
|
||||
HdlNone => connect(firing, false),
|
||||
HdlSome(_) => connect(firing, expr.ready),
|
||||
}
|
||||
firing
|
||||
}
|
||||
#[hdl]
|
||||
pub fn firing_data(expr: impl ToExpr<Type = Self>) -> Expr<HdlOption<T>> {
|
||||
let expr = expr.to_expr();
|
||||
let option_ty = Expr::ty(expr).data;
|
||||
#[hdl]
|
||||
let firing_data = wire(option_ty);
|
||||
connect(firing_data, option_ty.HdlNone());
|
||||
#[hdl]
|
||||
if expr.ready {
|
||||
connect(firing_data, expr.data);
|
||||
}
|
||||
firing_data
|
||||
}
|
||||
#[hdl]
|
||||
pub fn map<R: Type>(
|
||||
expr: Expr<Self>,
|
||||
f: impl FnOnce(Expr<T>) -> Expr<R>,
|
||||
) -> Expr<ReadyValid<R>> {
|
||||
let data = HdlOption::map(expr.data, f);
|
||||
#[hdl]
|
||||
let mapped = wire(ReadyValid[Expr::ty(data).HdlSome]);
|
||||
connect(mapped.data, data);
|
||||
connect(expr.ready, mapped.ready);
|
||||
mapped
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl_module]
|
||||
pub fn queue<T: Type>(
|
||||
ty: T,
|
||||
capacity: NonZeroUsize,
|
||||
inp_ready_is_comb: bool,
|
||||
out_valid_is_comb: bool,
|
||||
) {
|
||||
let count_ty = UInt::range_inclusive(0..=capacity.get());
|
||||
let index_ty = UInt::range(0..capacity.get());
|
||||
|
||||
#[hdl]
|
||||
let cd: ClockDomain = m.input();
|
||||
#[hdl]
|
||||
let inp: ReadyValid<T> = m.input(ReadyValid[ty]);
|
||||
#[hdl]
|
||||
let out: ReadyValid<T> = m.output(ReadyValid[ty]);
|
||||
#[hdl]
|
||||
let count: UInt = m.output(count_ty);
|
||||
|
||||
#[hdl]
|
||||
let inp_index_reg = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty));
|
||||
#[hdl]
|
||||
let out_index_reg = reg_builder().clock_domain(cd).reset(0.cast_to(index_ty));
|
||||
#[hdl]
|
||||
let maybe_full_reg = reg_builder().clock_domain(cd).reset(false);
|
||||
|
||||
#[hdl]
|
||||
let mut mem = memory(ty);
|
||||
mem.depth(capacity.get());
|
||||
let read_port = mem.new_read_port();
|
||||
let write_port = mem.new_write_port();
|
||||
|
||||
#[hdl]
|
||||
let inp_firing: Bool = wire();
|
||||
connect(inp_firing, ReadyValid::firing(inp));
|
||||
#[hdl]
|
||||
let out_firing: Bool = wire();
|
||||
connect(out_firing, ReadyValid::firing(out));
|
||||
#[hdl]
|
||||
let indexes_equal: Bool = wire();
|
||||
connect(indexes_equal, inp_index_reg.cmp_eq(out_index_reg));
|
||||
#[hdl]
|
||||
let empty: Bool = wire();
|
||||
connect(empty, indexes_equal & !maybe_full_reg);
|
||||
#[hdl]
|
||||
let full: Bool = wire();
|
||||
connect(full, indexes_equal & maybe_full_reg);
|
||||
|
||||
connect(read_port.addr, out_index_reg);
|
||||
connect(read_port.en, true);
|
||||
connect(read_port.clk, cd.clk);
|
||||
connect(write_port.addr, inp_index_reg);
|
||||
connect(write_port.en, inp_firing);
|
||||
connect(write_port.clk, cd.clk);
|
||||
connect(write_port.data, HdlOption::unwrap_or(inp.data, ty.uninit()));
|
||||
connect(write_port.mask, splat_mask(ty, true.to_expr()));
|
||||
|
||||
connect(inp.ready, !full);
|
||||
if inp_ready_is_comb {
|
||||
#[hdl]
|
||||
if out.ready {
|
||||
connect(inp.ready, true);
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
if !empty {
|
||||
connect(out.data, HdlSome(read_port.data));
|
||||
} else {
|
||||
if out_valid_is_comb {
|
||||
connect(out.data, inp.data);
|
||||
} else {
|
||||
connect(out.data, HdlOption[ty].HdlNone());
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
if inp_firing.cmp_ne(out_firing) {
|
||||
connect(maybe_full_reg, inp_firing);
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
if inp_firing {
|
||||
#[hdl]
|
||||
if inp_index_reg.cmp_eq(capacity.get() - 1) {
|
||||
connect_any(inp_index_reg, 0_hdl_u0);
|
||||
} else {
|
||||
connect_any(inp_index_reg, inp_index_reg + 1_hdl_u1);
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
if out_firing {
|
||||
#[hdl]
|
||||
if out_index_reg.cmp_eq(capacity.get() - 1) {
|
||||
connect_any(out_index_reg, 0_hdl_u0);
|
||||
} else {
|
||||
connect_any(out_index_reg, out_index_reg + 1_hdl_u1);
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
if indexes_equal {
|
||||
#[hdl]
|
||||
if maybe_full_reg {
|
||||
connect_any(count, capacity);
|
||||
} else {
|
||||
connect_any(count, 0_hdl_u0);
|
||||
}
|
||||
} else {
|
||||
if capacity.is_power_of_two() {
|
||||
debug_assert_eq!(count_ty.width(), index_ty.width() + 1);
|
||||
#[hdl]
|
||||
let count_lower = wire(index_ty);
|
||||
connect(
|
||||
count_lower,
|
||||
(inp_index_reg - out_index_reg).cast_to(index_ty),
|
||||
); // wrap
|
||||
connect(count, count_lower.cast_to(count_ty));
|
||||
} else {
|
||||
debug_assert_eq!(count_ty.width(), index_ty.width());
|
||||
#[hdl]
|
||||
if inp_index_reg.cmp_lt(out_index_reg) {
|
||||
connect(count, inp_index_reg + capacity - out_index_reg);
|
||||
} else {
|
||||
connect(count, inp_index_reg - out_index_reg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{
|
||||
cli::FormalMode, firrtl::ExportOptions,
|
||||
module::transform::simplify_enums::SimplifyEnumsKind, testing::assert_formal,
|
||||
ty::StaticType,
|
||||
};
|
||||
use std::num::NonZero;
|
||||
|
||||
#[track_caller]
|
||||
fn test_queue(capacity: NonZeroUsize, inp_ready_is_comb: bool, out_valid_is_comb: bool) {
|
||||
assert_formal(
|
||||
format_args!("test_queue_{capacity}_{inp_ready_is_comb}_{out_valid_is_comb}"),
|
||||
queue_test(capacity, inp_ready_is_comb, out_valid_is_comb),
|
||||
FormalMode::Prove,
|
||||
14,
|
||||
None,
|
||||
ExportOptions {
|
||||
simplify_enums: Some(SimplifyEnumsKind::ReplaceWithBundleOfUInts),
|
||||
..ExportOptions::default()
|
||||
},
|
||||
);
|
||||
#[hdl_module]
|
||||
fn queue_test(capacity: NonZeroUsize, inp_ready_is_comb: bool, out_valid_is_comb: bool) {
|
||||
#[hdl]
|
||||
let clk: Clock = m.input();
|
||||
#[hdl]
|
||||
let cd = wire();
|
||||
connect(
|
||||
cd,
|
||||
#[hdl]
|
||||
ClockDomain {
|
||||
clk,
|
||||
rst: formal_reset().to_reset(),
|
||||
},
|
||||
);
|
||||
#[hdl]
|
||||
let inp_data: HdlOption<UInt<8>> = wire();
|
||||
#[hdl]
|
||||
if any_seq(Bool) {
|
||||
connect(inp_data, HdlSome(any_seq(UInt::<8>::TYPE)));
|
||||
} else {
|
||||
connect(inp_data, HdlNone());
|
||||
}
|
||||
#[hdl]
|
||||
let out_ready: Bool = wire();
|
||||
connect(out_ready, any_seq(Bool));
|
||||
let index_ty: UInt<32> = UInt::TYPE;
|
||||
#[hdl]
|
||||
let index_to_check = wire();
|
||||
connect(index_to_check, any_const(index_ty));
|
||||
let index_max = !index_ty.zero();
|
||||
// we saturate at index_max, so only check indexes where we properly maintain position
|
||||
hdl_assume(clk, index_to_check.cmp_ne(index_max), "");
|
||||
#[hdl]
|
||||
let dut = instance(queue(
|
||||
UInt[ConstUsize::<8>],
|
||||
capacity,
|
||||
inp_ready_is_comb,
|
||||
out_valid_is_comb,
|
||||
));
|
||||
connect(dut.cd, cd);
|
||||
connect(dut.inp.data, inp_data);
|
||||
connect(dut.out.ready, out_ready);
|
||||
hdl_assume(
|
||||
clk,
|
||||
index_to_check.cmp_ne(!Expr::ty(index_to_check).zero()),
|
||||
"",
|
||||
);
|
||||
|
||||
#[hdl]
|
||||
let expected_count_reg = reg_builder().clock_domain(cd).reset(0u32);
|
||||
#[hdl]
|
||||
let next_expected_count = wire();
|
||||
connect(next_expected_count, expected_count_reg);
|
||||
connect(expected_count_reg, next_expected_count);
|
||||
#[hdl]
|
||||
if ReadyValid::firing(dut.inp) & !ReadyValid::firing(dut.out) {
|
||||
connect_any(next_expected_count, expected_count_reg + 1u8);
|
||||
} else if !ReadyValid::firing(dut.inp) & ReadyValid::firing(dut.out) {
|
||||
connect_any(next_expected_count, expected_count_reg - 1u8);
|
||||
}
|
||||
hdl_assert(cd.clk, expected_count_reg.cmp_eq(dut.count), "");
|
||||
|
||||
#[hdl]
|
||||
let prev_out_ready_reg = reg_builder().clock_domain(cd).reset(!0_hdl_u3);
|
||||
connect_any(
|
||||
prev_out_ready_reg,
|
||||
(prev_out_ready_reg << 1) | out_ready.cast_to(UInt[1]),
|
||||
);
|
||||
#[hdl]
|
||||
let prev_inp_valid_reg = reg_builder().clock_domain(cd).reset(!0_hdl_u3);
|
||||
connect_any(
|
||||
prev_inp_valid_reg,
|
||||
(prev_inp_valid_reg << 1) | HdlOption::is_some(inp_data).cast_to(UInt[1]),
|
||||
);
|
||||
hdl_assume(
|
||||
clk,
|
||||
(prev_out_ready_reg & prev_inp_valid_reg).cmp_ne(0u8),
|
||||
"",
|
||||
);
|
||||
|
||||
#[hdl]
|
||||
let inp_index_reg = reg_builder().clock_domain(cd).reset(index_ty.zero());
|
||||
#[hdl]
|
||||
let stored_inp_data_reg = reg_builder().clock_domain(cd).reset(0u8);
|
||||
|
||||
#[hdl]
|
||||
if let HdlSome(data) = ReadyValid::firing_data(dut.inp) {
|
||||
#[hdl]
|
||||
if inp_index_reg.cmp_lt(index_max) {
|
||||
connect_any(inp_index_reg, inp_index_reg + 1u8);
|
||||
#[hdl]
|
||||
if inp_index_reg.cmp_eq(index_to_check) {
|
||||
connect(stored_inp_data_reg, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
if inp_index_reg.cmp_lt(index_to_check) {
|
||||
hdl_assert(clk, stored_inp_data_reg.cmp_eq(0u8), "");
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
let out_index_reg = reg_builder().clock_domain(cd).reset(index_ty.zero());
|
||||
#[hdl]
|
||||
let stored_out_data_reg = reg_builder().clock_domain(cd).reset(0u8);
|
||||
|
||||
#[hdl]
|
||||
if let HdlSome(data) = ReadyValid::firing_data(dut.out) {
|
||||
#[hdl]
|
||||
if out_index_reg.cmp_lt(index_max) {
|
||||
connect_any(out_index_reg, out_index_reg + 1u8);
|
||||
#[hdl]
|
||||
if out_index_reg.cmp_eq(index_to_check) {
|
||||
connect(stored_out_data_reg, data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
if out_index_reg.cmp_lt(index_to_check) {
|
||||
hdl_assert(clk, stored_out_data_reg.cmp_eq(0u8), "");
|
||||
}
|
||||
|
||||
hdl_assert(clk, inp_index_reg.cmp_ge(out_index_reg), "");
|
||||
|
||||
#[hdl]
|
||||
if inp_index_reg.cmp_lt(index_max) & out_index_reg.cmp_lt(index_max) {
|
||||
hdl_assert(
|
||||
clk,
|
||||
expected_count_reg.cmp_eq(inp_index_reg - out_index_reg),
|
||||
"",
|
||||
);
|
||||
} else {
|
||||
hdl_assert(
|
||||
clk,
|
||||
expected_count_reg.cmp_ge(inp_index_reg - out_index_reg),
|
||||
"",
|
||||
);
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
if inp_index_reg.cmp_gt(index_to_check) & out_index_reg.cmp_gt(index_to_check) {
|
||||
hdl_assert(clk, stored_inp_data_reg.cmp_eq(stored_out_data_reg), "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_1_false_false() {
|
||||
test_queue(NonZero::new(1).unwrap(), false, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_1_false_true() {
|
||||
test_queue(NonZero::new(1).unwrap(), false, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_1_true_false() {
|
||||
test_queue(NonZero::new(1).unwrap(), true, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_1_true_true() {
|
||||
test_queue(NonZero::new(1).unwrap(), true, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_2_false_false() {
|
||||
test_queue(NonZero::new(2).unwrap(), false, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_2_false_true() {
|
||||
test_queue(NonZero::new(2).unwrap(), false, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_2_true_false() {
|
||||
test_queue(NonZero::new(2).unwrap(), true, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_2_true_true() {
|
||||
test_queue(NonZero::new(2).unwrap(), true, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_3_false_false() {
|
||||
test_queue(NonZero::new(3).unwrap(), false, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_3_false_true() {
|
||||
test_queue(NonZero::new(3).unwrap(), false, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_3_true_false() {
|
||||
test_queue(NonZero::new(3).unwrap(), true, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_3_true_true() {
|
||||
test_queue(NonZero::new(3).unwrap(), true, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_4_false_false() {
|
||||
test_queue(NonZero::new(4).unwrap(), false, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_4_false_true() {
|
||||
test_queue(NonZero::new(4).unwrap(), false, true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_4_true_false() {
|
||||
test_queue(NonZero::new(4).unwrap(), true, false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_4_true_true() {
|
||||
test_queue(NonZero::new(4).unwrap(), true, true);
|
||||
}
|
||||
}
|
|
@ -1,114 +0,0 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
mod safety_boundary {
|
||||
use std::{cell::Cell, ptr::NonNull};
|
||||
|
||||
pub(super) struct Impl<T: ?Sized>(Cell<Option<NonNull<T>>>);
|
||||
|
||||
impl<T: ?Sized> Impl<T> {
|
||||
#[inline]
|
||||
pub(super) const fn new() -> Self {
|
||||
Self(Cell::new(None))
|
||||
}
|
||||
#[inline]
|
||||
pub(super) const fn opt_static_ref(v: Option<&'static T>) -> Self {
|
||||
Self(Cell::new(if let Some(v) = v {
|
||||
// SAFETY: v is a valid reference for lifetime 'static
|
||||
unsafe { Some(NonNull::new_unchecked(v as *const T as *mut T)) }
|
||||
} else {
|
||||
None
|
||||
}))
|
||||
}
|
||||
/// set `self` to `value` for the duration of the `f()` call.
|
||||
#[inline]
|
||||
pub(super) fn set_opt<F: FnOnce() -> R, R>(&self, value: Option<&T>, f: F) -> R {
|
||||
struct ResetOnDrop<'a, T: ?Sized> {
|
||||
this: &'a Impl<T>,
|
||||
old: Option<NonNull<T>>,
|
||||
}
|
||||
impl<T: ?Sized> Drop for ResetOnDrop<'_, T> {
|
||||
fn drop(&mut self) {
|
||||
self.this.0.set(self.old);
|
||||
}
|
||||
}
|
||||
// reset to old value before exiting this function ensuring `self`
|
||||
// is not set to `value` when its lifetime is expired
|
||||
let _reset_on_drop = ResetOnDrop {
|
||||
this: self,
|
||||
old: self.0.replace(value.map(NonNull::from)),
|
||||
};
|
||||
f()
|
||||
}
|
||||
#[inline]
|
||||
pub(super) fn get_ptr(&self) -> Option<NonNull<T>> {
|
||||
self.0.get()
|
||||
}
|
||||
/// get the reference in `self` for the duration of the `f(...)` call.
|
||||
#[inline]
|
||||
pub(super) fn with_opt<F: for<'a> FnOnce(Option<&'a T>) -> R, R>(&self, f: F) -> R {
|
||||
// SAFETY:
|
||||
// `self.0` is only `Some` when inside some `set_opt` call or when set
|
||||
// to some `&'static T`, which ensures that the pointer is live and valid.
|
||||
//
|
||||
// the reference we give away has its lifetime scoped to this
|
||||
// function call which ensures that it won't escape
|
||||
unsafe { f(self.0.get().map(|v| &*v.as_ptr())) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// holds a `Cell<Option<&'scoped T>>` where `'scoped` is erased. This is useful for holding references in TLS.
|
||||
pub struct ScopedRef<T: ?Sized>(safety_boundary::Impl<T>);
|
||||
|
||||
impl<T: ?Sized> ScopedRef<T> {
|
||||
/// create a new empty [`ScopedRef`]
|
||||
#[inline]
|
||||
pub const fn new() -> Self {
|
||||
Self(safety_boundary::Impl::new())
|
||||
}
|
||||
#[inline]
|
||||
pub const fn opt_static_ref(v: Option<&'static T>) -> Self {
|
||||
Self(safety_boundary::Impl::opt_static_ref(v))
|
||||
}
|
||||
#[inline]
|
||||
pub const fn static_ref(v: &'static T) -> Self {
|
||||
Self::opt_static_ref(Some(v))
|
||||
}
|
||||
/// set `self` to `value` for the duration of the `f()` call.
|
||||
#[inline]
|
||||
pub fn set_opt<F: FnOnce() -> R, R>(&self, value: Option<&T>, f: F) -> R {
|
||||
self.0.set_opt(value, f)
|
||||
}
|
||||
/// set `self` to `value` for the duration of the `f()` call.
|
||||
#[inline]
|
||||
pub fn set<F: FnOnce() -> R, R>(&self, value: &T, f: F) -> R {
|
||||
self.0.set_opt(Some(value), f)
|
||||
}
|
||||
#[inline]
|
||||
pub fn is_some(&self) -> bool {
|
||||
self.0.get_ptr().is_some()
|
||||
}
|
||||
#[inline]
|
||||
pub fn is_none(&self) -> bool {
|
||||
self.0.get_ptr().is_none()
|
||||
}
|
||||
/// get the reference in `self` for the duration of the `f(...)` call. panics if no reference is set.
|
||||
#[inline]
|
||||
pub fn with<F: FnOnce(&T) -> R, R>(&self, f: F) -> R {
|
||||
self.0.with_opt(
|
||||
#[inline]
|
||||
|v| f(v.expect("called ScopedRef::with on an empty ScopedRef")),
|
||||
)
|
||||
}
|
||||
/// get the reference in `self` for the duration of the `f(...)` call.
|
||||
#[inline]
|
||||
pub fn with_opt<F: FnOnce(Option<&T>) -> R, R>(&self, f: F) -> R {
|
||||
self.0.with_opt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: ?Sized> Default for ScopedRef<T> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use std::{
|
||||
io::{self, BufRead},
|
||||
str,
|
||||
};
|
||||
|
||||
pub(crate) fn streaming_read_utf8<R: BufRead, E: From<io::Error>>(
|
||||
reader: R,
|
||||
mut callback: impl FnMut(&str) -> Result<(), E>,
|
||||
) -> Result<(), E> {
|
||||
let mut buf = [0; 4];
|
||||
let mut buf_len = 0;
|
||||
for byte in reader.bytes() {
|
||||
buf[buf_len] = byte?;
|
||||
buf_len += 1;
|
||||
match str::from_utf8(&buf[..buf_len]) {
|
||||
Ok(buf) => {
|
||||
callback(buf)?;
|
||||
buf_len = 0;
|
||||
}
|
||||
Err(e) => {
|
||||
if e.error_len().is_some() {
|
||||
callback("\u{FFFD}")?; // replacement character
|
||||
buf_len = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
88
crates/fayalite/src/valueless.rs
Normal file
88
crates/fayalite/src/valueless.rs
Normal file
|
@ -0,0 +1,88 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
int::{DynIntType, DynSIntType, DynUIntType, IntTypeTrait, SIntType},
|
||||
ty::{Type, Value},
|
||||
};
|
||||
use std::ops::RangeBounds;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, Default)]
|
||||
pub struct Valueless<T> {
|
||||
pub ty: T,
|
||||
}
|
||||
|
||||
impl<T: Type> Valueless<T> {
|
||||
pub fn to_canonical(&self) -> Valueless<T::CanonicalType> {
|
||||
Valueless {
|
||||
ty: self.ty.canonical(),
|
||||
}
|
||||
}
|
||||
pub fn from_canonical(v: Valueless<T::CanonicalType>) -> Self {
|
||||
Valueless {
|
||||
ty: T::from_canonical_type(v.ty),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
pub trait Sealed {}
|
||||
}
|
||||
|
||||
pub trait ValuelessTr: sealed::Sealed {
|
||||
type Type: Type<Value = Self::Value>;
|
||||
type Value: Value<Type = Self::Type>;
|
||||
}
|
||||
|
||||
impl<T> sealed::Sealed for Valueless<T> {}
|
||||
|
||||
impl<T: Type> ValuelessTr for Valueless<T> {
|
||||
type Type = T;
|
||||
type Value = T::Value;
|
||||
}
|
||||
|
||||
impl<T: IntTypeTrait> Valueless<T> {
|
||||
pub fn signum(&self) -> Valueless<SIntType<2>> {
|
||||
Valueless::default()
|
||||
}
|
||||
pub fn as_same_width_uint(self) -> Valueless<T::SameWidthUInt> {
|
||||
Valueless {
|
||||
ty: self.ty.as_same_width_uint(),
|
||||
}
|
||||
}
|
||||
pub fn as_same_width_sint(self) -> Valueless<T::SameWidthSInt> {
|
||||
Valueless {
|
||||
ty: self.ty.as_same_width_sint(),
|
||||
}
|
||||
}
|
||||
pub fn as_same_value_uint(self) -> Valueless<DynUIntType> {
|
||||
Valueless {
|
||||
ty: self.ty.as_same_value_uint(),
|
||||
}
|
||||
}
|
||||
pub fn as_same_value_sint(self) -> Valueless<DynSIntType> {
|
||||
Valueless {
|
||||
ty: self.ty.as_same_value_sint(),
|
||||
}
|
||||
}
|
||||
pub fn concat<HighType: IntTypeTrait>(
|
||||
&self,
|
||||
high_part: Valueless<HighType>,
|
||||
) -> Valueless<DynIntType<HighType::Signed>> {
|
||||
let ty = DynIntType::new(
|
||||
self.ty
|
||||
.width()
|
||||
.checked_add(high_part.ty.width())
|
||||
.expect("result has too many bits"),
|
||||
);
|
||||
Valueless { ty }
|
||||
}
|
||||
pub fn repeat(&self, count: usize) -> Valueless<DynIntType<T::Signed>> {
|
||||
let width = self.ty.width();
|
||||
let ty = DynIntType::new(width.checked_mul(count).expect("result has too many bits"));
|
||||
Valueless { ty }
|
||||
}
|
||||
pub fn slice<I: RangeBounds<usize>>(&self, index: I) -> Valueless<DynUIntType> {
|
||||
let ty = self.ty.slice(index);
|
||||
Valueless { ty }
|
||||
}
|
||||
}
|
|
@ -1,15 +1,15 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use crate::{
|
||||
expr::{Expr, Flow, ToExpr},
|
||||
expr::{Expr, ExprTrait, Flow, ToExpr},
|
||||
intern::Interned,
|
||||
module::{IncompleteDeclaration, NameId, ScopedNameId, StmtDeclaration, StmtWire},
|
||||
module::{NameId, ScopedNameId},
|
||||
source_location::SourceLocation,
|
||||
ty::{CanonicalType, Type},
|
||||
ty::{DynCanonicalType, DynType, Type},
|
||||
};
|
||||
use std::{cell::RefCell, fmt, rc::Rc};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
|
||||
#[derive(Clone, Eq, PartialEq, Hash)]
|
||||
pub struct Wire<T: Type> {
|
||||
name: ScopedNameId,
|
||||
source_location: SourceLocation,
|
||||
|
@ -18,14 +18,27 @@ pub struct Wire<T: Type> {
|
|||
|
||||
impl<T: Type + fmt::Debug> fmt::Debug for Wire<T> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "Wire({:?}: ", self.name)?;
|
||||
self.ty.fmt(f)?;
|
||||
f.write_str(")")
|
||||
f.debug_struct("Wire")
|
||||
.field("name", &self.name)
|
||||
.field("ty", &self.ty)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type> ToExpr for Wire<T> {
|
||||
type Type = T;
|
||||
|
||||
fn ty(&self) -> Self::Type {
|
||||
self.ty.clone()
|
||||
}
|
||||
|
||||
fn to_expr(&self) -> Expr<<Self::Type as Type>::Value> {
|
||||
Expr::new_unchecked(self.expr_enum())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Type> Wire<T> {
|
||||
pub fn canonical(&self) -> Wire<CanonicalType> {
|
||||
pub fn canonical(&self) -> Wire<T::CanonicalType> {
|
||||
let Self {
|
||||
name,
|
||||
source_location,
|
||||
|
@ -37,8 +50,29 @@ impl<T: Type> Wire<T> {
|
|||
ty: ty.canonical(),
|
||||
}
|
||||
}
|
||||
pub fn ty(&self) -> T {
|
||||
self.ty
|
||||
pub fn to_dyn_wire(&self) -> Wire<Interned<dyn DynType>> {
|
||||
let Self {
|
||||
name,
|
||||
source_location,
|
||||
ref ty,
|
||||
} = *self;
|
||||
Wire {
|
||||
name,
|
||||
source_location,
|
||||
ty: ty.to_dyn(),
|
||||
}
|
||||
}
|
||||
pub fn to_dyn_canonical_wire(&self) -> Wire<Interned<dyn DynCanonicalType>> {
|
||||
let Self {
|
||||
name,
|
||||
source_location,
|
||||
ref ty,
|
||||
} = *self;
|
||||
Wire {
|
||||
name,
|
||||
source_location,
|
||||
ty: ty.canonical_dyn(),
|
||||
}
|
||||
}
|
||||
pub fn new_unchecked(
|
||||
scoped_name: ScopedNameId,
|
||||
|
@ -76,57 +110,3 @@ impl<T: Type> Wire<T> {
|
|||
true
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct IncompleteWire {
|
||||
pub(crate) declaration: Rc<RefCell<IncompleteDeclaration>>,
|
||||
}
|
||||
|
||||
impl IncompleteWire {
|
||||
#[track_caller]
|
||||
pub fn complete<T: Type>(&mut self, ty: T) -> Expr<T> {
|
||||
let canonical_type = ty.canonical();
|
||||
let mut declaration = self.declaration.borrow_mut();
|
||||
if let IncompleteDeclaration::Incomplete {
|
||||
name,
|
||||
source_location,
|
||||
} = *declaration
|
||||
{
|
||||
*declaration = IncompleteDeclaration::Complete(
|
||||
StmtWire {
|
||||
annotations: (),
|
||||
wire: Wire {
|
||||
name,
|
||||
source_location,
|
||||
ty: canonical_type,
|
||||
},
|
||||
}
|
||||
.into(),
|
||||
);
|
||||
}
|
||||
match *declaration {
|
||||
IncompleteDeclaration::Complete(StmtDeclaration::Wire(StmtWire {
|
||||
wire:
|
||||
Wire {
|
||||
name,
|
||||
source_location,
|
||||
ty: wire_ty,
|
||||
},
|
||||
..
|
||||
})) => {
|
||||
drop(declaration);
|
||||
assert_eq!(wire_ty, canonical_type, "type mismatch");
|
||||
Wire {
|
||||
name,
|
||||
source_location,
|
||||
ty,
|
||||
}
|
||||
.to_expr()
|
||||
}
|
||||
IncompleteDeclaration::Taken => panic!("can't use wire outside of containing module"),
|
||||
IncompleteDeclaration::Complete(_) | IncompleteDeclaration::Incomplete { .. } => {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,133 +0,0 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
//! Formal tests in Fayalite
|
||||
|
||||
use fayalite::{
|
||||
cli::FormalMode,
|
||||
clock::{Clock, ClockDomain},
|
||||
expr::{CastTo, HdlPartialEq},
|
||||
firrtl::ExportOptions,
|
||||
formal::{any_seq, formal_reset, hdl_assert, hdl_assume},
|
||||
hdl_module,
|
||||
int::{Bool, UInt},
|
||||
module::{connect, connect_any, reg_builder, wire},
|
||||
reset::ToReset,
|
||||
testing::assert_formal,
|
||||
};
|
||||
|
||||
/// Test hidden state
|
||||
///
|
||||
/// Hidden state can cause problems for induction, since the formal engine
|
||||
/// can assign invalid values to the state registers, making it traverse
|
||||
/// valid but unreachable states.
|
||||
///
|
||||
/// One solution is to go sufficiently in the past so the engine is forced
|
||||
/// to eventually take a reachable state. This may be hampered by
|
||||
/// existence of loops, then assumptions may be added to break them.
|
||||
///
|
||||
/// Another solution is to "open the black box" and add additional
|
||||
/// assertions involving the hidden state, so that the unreachable states
|
||||
/// become invalid as well.
|
||||
///
|
||||
/// Both approaches are taken here.
|
||||
///
|
||||
/// See [Claire Wolf's presentation] and [Zipcpu blog article].
|
||||
///
|
||||
/// [Claire Wolf's presentation]: https://web.archive.org/web/20200115081517fw_/http://www.clifford.at/papers/2017/smtbmc-sby/
|
||||
/// [Zipcpu blog article]: https://zipcpu.com/blog/2018/03/10/induction-exercise.html
|
||||
mod hidden_state {
|
||||
use super::*;
|
||||
/// Test hidden state by shift registers
|
||||
///
|
||||
/// The code implement the ideas from an article in the [Zipcpu blog]. Two
|
||||
/// shift registers are fed from the same input, so they should always have
|
||||
/// the same value. However the only observable is a comparison of their
|
||||
/// last bit, all the others are hidden. To complicate matters, an enable
|
||||
/// signal causes a loop in state space.
|
||||
///
|
||||
/// [Zipcpu blog]: https://zipcpu.com/blog/2018/03/10/induction-exercise.html
|
||||
#[test]
|
||||
fn shift_register() {
|
||||
enum ConstraintMode {
|
||||
WithExtraAssertions,
|
||||
WithExtraAssumptions,
|
||||
}
|
||||
use ConstraintMode::*;
|
||||
#[hdl_module]
|
||||
fn test_module(constraint_mode: ConstraintMode) {
|
||||
#[hdl]
|
||||
let clk: Clock = m.input();
|
||||
#[hdl]
|
||||
let cd = wire();
|
||||
connect(
|
||||
cd,
|
||||
#[hdl]
|
||||
ClockDomain {
|
||||
clk,
|
||||
rst: formal_reset().to_reset(),
|
||||
},
|
||||
);
|
||||
// input signal for the shift registers
|
||||
#[hdl]
|
||||
let i: Bool = wire();
|
||||
connect(i, any_seq(Bool));
|
||||
// shift enable signal
|
||||
#[hdl]
|
||||
let en: Bool = wire();
|
||||
connect(en, any_seq(Bool));
|
||||
// comparison output
|
||||
#[hdl]
|
||||
let o: Bool = wire();
|
||||
// shift registers, with enable
|
||||
#[hdl]
|
||||
let r1 = reg_builder().clock_domain(cd).reset(0u8);
|
||||
#[hdl]
|
||||
let r2 = reg_builder().clock_domain(cd).reset(0u8);
|
||||
#[hdl]
|
||||
if en {
|
||||
connect_any(r1, (r1 << 1) | i.cast_to(UInt[1]));
|
||||
connect_any(r2, (r2 << 1) | i.cast_to(UInt[1]));
|
||||
}
|
||||
// compare last bits of both shift registers
|
||||
connect(o, r1[7].cmp_eq(r2[7]));
|
||||
|
||||
// what we want to prove: last bits are always equal
|
||||
hdl_assert(clk, o, "");
|
||||
|
||||
// additional terms below are only needed to assist with the induction proof
|
||||
match constraint_mode {
|
||||
WithExtraAssertions => {
|
||||
// "Open the box": add assertions about hidden state.
|
||||
// In this case, the hidden bits are also always equal.
|
||||
hdl_assert(clk, r1.cmp_eq(r2), "");
|
||||
}
|
||||
WithExtraAssumptions => {
|
||||
// Break the loop, do not allow "en" to remain low forever
|
||||
#[hdl]
|
||||
let past_en_reg = reg_builder().clock_domain(cd).reset(false);
|
||||
connect(past_en_reg, en);
|
||||
hdl_assume(clk, past_en_reg | en, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
// we need a minimum of 16 steps so we can constrain all eight shift register bits,
|
||||
// given that we are allowed to disable the shift once every two cycles.
|
||||
assert_formal(
|
||||
"shift_register_with_assumptions",
|
||||
test_module(WithExtraAssumptions),
|
||||
FormalMode::Prove,
|
||||
16,
|
||||
None,
|
||||
ExportOptions::default(),
|
||||
);
|
||||
// here a couple of cycles is enough
|
||||
assert_formal(
|
||||
"shift_register_with_assertions",
|
||||
test_module(WithExtraAssertions),
|
||||
FormalMode::Prove,
|
||||
2,
|
||||
None,
|
||||
ExportOptions::default(),
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,193 +0,0 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use fayalite::{
|
||||
bundle::BundleType,
|
||||
enum_::EnumType,
|
||||
int::{BoolOrIntType, IntType},
|
||||
prelude::*,
|
||||
ty::StaticType,
|
||||
};
|
||||
use std::marker::PhantomData;
|
||||
|
||||
#[hdl(outline_generated)]
|
||||
pub struct S<T, Len: Size, T2> {
|
||||
pub a: T,
|
||||
b: UInt<3>,
|
||||
pub(crate) c: ArrayType<UInt<1>, Len>,
|
||||
pub d: T2,
|
||||
pub _phantom: PhantomData<(T, Len)>,
|
||||
}
|
||||
|
||||
#[hdl(outline_generated)]
|
||||
pub struct S3<const LEN: usize, T> {
|
||||
pub a: T,
|
||||
b: UInt<3>,
|
||||
pub(crate) c: Array<UInt<1>, LEN>,
|
||||
pub d: S<T, ConstUsize<LEN>, ()>,
|
||||
}
|
||||
|
||||
#[hdl(outline_generated)]
|
||||
pub enum E<T> {
|
||||
A,
|
||||
B(UInt<3>),
|
||||
C(T),
|
||||
D(TyAlias2),
|
||||
E(TyAlias<Bool, ConstUsize<1>, { 1 + 2 }>),
|
||||
}
|
||||
|
||||
#[hdl(outline_generated)]
|
||||
pub struct S2<T = ()> {
|
||||
pub v: E<T>,
|
||||
}
|
||||
|
||||
#[hdl(outline_generated)]
|
||||
pub type TyAlias<T, Sz: Size, const C: usize, D = ()> = Array<S<T, Sz, D>, C>;
|
||||
|
||||
#[hdl(outline_generated)]
|
||||
pub type TyAlias2 = TyAlias<UInt<8>, ConstUsize<24>, 5>;
|
||||
|
||||
// check that #[hdl] properly handles hygiene
|
||||
macro_rules! types_in_macros {
|
||||
($a:ident, $b:ident, $c:ident, $d:ident, $e:ident, $f:ident, $A:ident, $B:ident, $C:ident, $D:ident, $E:ident, $F:ident) => {
|
||||
#[hdl]
|
||||
struct $F {}
|
||||
#[hdl]
|
||||
struct $A<$B, $C: Size, const $D: usize, $E = $F> {
|
||||
$a: $B,
|
||||
$b: UIntType<$C>,
|
||||
$c: SInt<$D>,
|
||||
$d: HdlOption<$E>,
|
||||
$e: $E,
|
||||
$f: $F,
|
||||
}
|
||||
#[allow(non_camel_case_types)]
|
||||
#[hdl]
|
||||
enum $B<$C: Size, const $D: usize, $E = $F> {
|
||||
$a($A<(), $C, $D, $E>),
|
||||
$b(UIntType<$C>),
|
||||
$c(SInt<$D>),
|
||||
$d,
|
||||
$e($E),
|
||||
$f($F),
|
||||
}
|
||||
};
|
||||
// ensure every identifier has different hygiene
|
||||
() => {
|
||||
types_in_macros!(a);
|
||||
};
|
||||
($a:ident) => {
|
||||
types_in_macros!($a, b);
|
||||
};
|
||||
($a:ident, $b:ident) => {
|
||||
types_in_macros!($a, $b, c);
|
||||
};
|
||||
($a:ident, $b:ident, $c:ident) => {
|
||||
types_in_macros!($a, $b, $c, d);
|
||||
};
|
||||
($a:ident, $b:ident, $c:ident, $d:ident) => {
|
||||
types_in_macros!($a, $b, $c, $d, e);
|
||||
};
|
||||
($a:ident, $b:ident, $c:ident, $d:ident, $e:ident) => {
|
||||
types_in_macros!($a, $b, $c, $d, $e, f);
|
||||
};
|
||||
($a:ident, $b:ident, $c:ident, $d:ident, $e:ident, $f:ident) => {
|
||||
types_in_macros!($a, $b, $c, $d, $e, $f, A);
|
||||
};
|
||||
($a:ident, $b:ident, $c:ident, $d:ident, $e:ident, $f:ident, $A:ident) => {
|
||||
types_in_macros!($a, $b, $c, $d, $e, $f, $A, B);
|
||||
};
|
||||
($a:ident, $b:ident, $c:ident, $d:ident, $e:ident, $f:ident, $A:ident, $B:ident) => {
|
||||
types_in_macros!($a, $b, $c, $d, $e, $f, $A, $B, C);
|
||||
};
|
||||
($a:ident, $b:ident, $c:ident, $d:ident, $e:ident, $f:ident, $A:ident, $B:ident, $C:ident) => {
|
||||
types_in_macros!($a, $b, $c, $d, $e, $f, $A, $B, $C, D);
|
||||
};
|
||||
($a:ident, $b:ident, $c:ident, $d:ident, $e:ident, $f:ident, $A:ident, $B:ident, $C:ident, $D:ident) => {
|
||||
types_in_macros!($a, $b, $c, $d, $e, $f, $A, $B, $C, $D, E);
|
||||
};
|
||||
($a:ident, $b:ident, $c:ident, $d:ident, $e:ident, $f:ident, $A:ident, $B:ident, $C:ident, $D:ident, $E:ident) => {
|
||||
types_in_macros!($a, $b, $c, $d, $e, $f, $A, $B, $C, $D, $E, F);
|
||||
};
|
||||
}
|
||||
|
||||
types_in_macros!();
|
||||
|
||||
mod bound_kind {
|
||||
use fayalite::prelude::*;
|
||||
|
||||
#[hdl]
|
||||
pub struct Type<T> {
|
||||
v: T,
|
||||
}
|
||||
|
||||
#[hdl]
|
||||
pub struct Size<T: ::fayalite::int::Size> {
|
||||
v: UIntType<T>,
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! check_bounds {
|
||||
($name:ident<$(#[$field:ident, $kind:ident] $var:ident: $($bound:ident +)*),*>) => {
|
||||
#[hdl(outline_generated)]
|
||||
struct $name<$($var: $($bound +)*,)*> {
|
||||
$($field: bound_kind::$kind<$var>,)*
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
check_bounds!(CheckBoundsS0<#[a, Size] A: Size +>);
|
||||
check_bounds!(CheckBoundsS1<#[a, Size] A: KnownSize +>);
|
||||
check_bounds!(CheckBoundsT0<#[a, Type] A: Type +>);
|
||||
check_bounds!(CheckBoundsT1<#[a, Type] A: BoolOrIntType +>);
|
||||
check_bounds!(CheckBoundsT2<#[a, Type] A: BundleType +>);
|
||||
check_bounds!(CheckBoundsT3<#[a, Type] A: EnumType +>);
|
||||
check_bounds!(CheckBoundsT4<#[a, Type] A: IntType +>);
|
||||
check_bounds!(CheckBoundsT5<#[a, Type] A: StaticType +>);
|
||||
check_bounds!(CheckBoundsSS0<#[a, Size] A: Size +, #[b, Size] B: Size +>);
|
||||
check_bounds!(CheckBoundsSS1<#[a, Size] A: KnownSize +, #[b, Size] B: Size +>);
|
||||
check_bounds!(CheckBoundsST0<#[a, Size] A: Size +, #[b, Type] B: Type +>);
|
||||
check_bounds!(CheckBoundsST1<#[a, Size] A: KnownSize +, #[b, Type] B: Type +>);
|
||||
check_bounds!(CheckBoundsTS0<#[a, Type] A: Type +, #[b, Size] B: Size +>);
|
||||
check_bounds!(CheckBoundsTS1<#[a, Type] A: BoolOrIntType +, #[b, Size] B: Size +>);
|
||||
check_bounds!(CheckBoundsTS2<#[a, Type] A: BundleType +, #[b, Size] B: Size +>);
|
||||
check_bounds!(CheckBoundsTS3<#[a, Type] A: EnumType +, #[b, Size] B: Size +>);
|
||||
check_bounds!(CheckBoundsTS4<#[a, Type] A: IntType +, #[b, Size] B: Size +>);
|
||||
check_bounds!(CheckBoundsTS5<#[a, Type] A: StaticType +, #[b, Size] B: Size +>);
|
||||
check_bounds!(CheckBoundsTT0<#[a, Type] A: Type +, #[b, Type] B: Type +>);
|
||||
check_bounds!(CheckBoundsTT1<#[a, Type] A: BoolOrIntType +, #[b, Type] B: Type +>);
|
||||
check_bounds!(CheckBoundsTT2<#[a, Type] A: BundleType +, #[b, Type] B: Type +>);
|
||||
check_bounds!(CheckBoundsTT3<#[a, Type] A: EnumType +, #[b, Type] B: Type +>);
|
||||
check_bounds!(CheckBoundsTT4<#[a, Type] A: IntType +, #[b, Type] B: Type +>);
|
||||
check_bounds!(CheckBoundsTT5<#[a, Type] A: StaticType +, #[b, Type] B: Type +>);
|
||||
check_bounds!(CheckBoundsSSS0<#[a, Size] A: Size +, #[b, Size] B: Size +, #[c, Size] C: Size +>);
|
||||
check_bounds!(CheckBoundsSSS1<#[a, Size] A: KnownSize +, #[b, Size] B: Size +, #[c, Size] C: Size +>);
|
||||
check_bounds!(CheckBoundsSST0<#[a, Size] A: Size +, #[b, Size] B: Size +, #[c, Type] C: Type +>);
|
||||
check_bounds!(CheckBoundsSST1<#[a, Size] A: KnownSize +, #[b, Size] B: Size +, #[c, Type] C: Type +>);
|
||||
check_bounds!(CheckBoundsSTS0<#[a, Size] A: Size +, #[b, Type] B: Type +, #[c, Size] C: Size +>);
|
||||
check_bounds!(CheckBoundsSTS1<#[a, Size] A: KnownSize +, #[b, Type] B: Type +, #[c, Size] C: Size +>);
|
||||
check_bounds!(CheckBoundsSTT0<#[a, Size] A: Size +, #[b, Type] B: Type +, #[c, Type] C: Type +>);
|
||||
check_bounds!(CheckBoundsSTT1<#[a, Size] A: KnownSize +, #[b, Type] B: Type +, #[c, Type] C: Type +>);
|
||||
check_bounds!(CheckBoundsTSS0<#[a, Type] A: Type +, #[b, Size] B: Size +, #[c, Size] C: Size +>);
|
||||
check_bounds!(CheckBoundsTSS1<#[a, Type] A: BoolOrIntType +, #[b, Size] B: Size +, #[c, Size] C: Size +>);
|
||||
check_bounds!(CheckBoundsTSS2<#[a, Type] A: BundleType +, #[b, Size] B: Size +, #[c, Size] C: Size +>);
|
||||
check_bounds!(CheckBoundsTSS3<#[a, Type] A: EnumType +, #[b, Size] B: Size +, #[c, Size] C: Size +>);
|
||||
check_bounds!(CheckBoundsTSS4<#[a, Type] A: IntType +, #[b, Size] B: Size +, #[c, Size] C: Size +>);
|
||||
check_bounds!(CheckBoundsTSS5<#[a, Type] A: StaticType +, #[b, Size] B: Size +, #[c, Size] C: Size +>);
|
||||
check_bounds!(CheckBoundsTST0<#[a, Type] A: Type +, #[b, Size] B: Size +, #[c, Type] C: Type +>);
|
||||
check_bounds!(CheckBoundsTST1<#[a, Type] A: BoolOrIntType +, #[b, Size] B: Size +, #[c, Type] C: Type +>);
|
||||
check_bounds!(CheckBoundsTST2<#[a, Type] A: BundleType +, #[b, Size] B: Size +, #[c, Type] C: Type +>);
|
||||
check_bounds!(CheckBoundsTST3<#[a, Type] A: EnumType +, #[b, Size] B: Size +, #[c, Type] C: Type +>);
|
||||
check_bounds!(CheckBoundsTST4<#[a, Type] A: IntType +, #[b, Size] B: Size +, #[c, Type] C: Type +>);
|
||||
check_bounds!(CheckBoundsTST5<#[a, Type] A: StaticType +, #[b, Size] B: Size +, #[c, Type] C: Type +>);
|
||||
check_bounds!(CheckBoundsTTS0<#[a, Type] A: Type +, #[b, Type] B: Type +, #[c, Size] C: Size +>);
|
||||
check_bounds!(CheckBoundsTTS1<#[a, Type] A: BoolOrIntType +, #[b, Type] B: Type +, #[c, Size] C: Size +>);
|
||||
check_bounds!(CheckBoundsTTS2<#[a, Type] A: BundleType +, #[b, Type] B: Type +, #[c, Size] C: Size +>);
|
||||
check_bounds!(CheckBoundsTTS3<#[a, Type] A: EnumType +, #[b, Type] B: Type +, #[c, Size] C: Size +>);
|
||||
check_bounds!(CheckBoundsTTS4<#[a, Type] A: IntType +, #[b, Type] B: Type +, #[c, Size] C: Size +>);
|
||||
check_bounds!(CheckBoundsTTS5<#[a, Type] A: StaticType +, #[b, Type] B: Type +, #[c, Size] C: Size +>);
|
||||
check_bounds!(CheckBoundsTTT0<#[a, Type] A: Type +, #[b, Type] B: Type +, #[c, Type] C: Type +>);
|
||||
check_bounds!(CheckBoundsTTT1<#[a, Type] A: BoolOrIntType +, #[b, Type] B: Type +, #[c, Type] C: Type +>);
|
||||
check_bounds!(CheckBoundsTTT2<#[a, Type] A: BundleType +, #[b, Type] B: Type +, #[c, Type] C: Type +>);
|
||||
check_bounds!(CheckBoundsTTT3<#[a, Type] A: EnumType +, #[b, Type] B: Type +, #[c, Type] C: Type +>);
|
||||
check_bounds!(CheckBoundsTTT4<#[a, Type] A: IntType +, #[b, Type] B: Type +, #[c, Type] C: Type +>);
|
||||
check_bounds!(CheckBoundsTTT5<#[a, Type] A: StaticType +, #[b, Type] B: Type +, #[c, Type] C: Type +>);
|
File diff suppressed because it is too large
Load diff
|
@ -1,7 +0,0 @@
|
|||
error: top-level #[hdl] can only be used on structs, enums, type aliases, or functions
|
||||
--> tests/ui/hdl_types.rs:5:1
|
||||
|
|
||||
5 | #[hdl]
|
||||
| ^^^^^^
|
||||
|
|
||||
= note: this error originates in the attribute macro `hdl` (in Nightly builds, run with -Z macro-backtrace for more info)
|
|
@ -1,8 +1,8 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use fayalite::hdl;
|
||||
use fayalite::ty::Value;
|
||||
|
||||
#[hdl]
|
||||
#[derive(Value)]
|
||||
union U {
|
||||
a: (),
|
||||
}
|
7
crates/fayalite/tests/ui/value_derive.stderr
Normal file
7
crates/fayalite/tests/ui/value_derive.stderr
Normal file
|
@ -0,0 +1,7 @@
|
|||
error: derive(Value) can only be used on structs or enums
|
||||
--> tests/ui/value_derive.rs:5:10
|
||||
|
|
||||
5 | #[derive(Value)]
|
||||
| ^^^^^
|
||||
|
|
||||
= note: this error originates in the derive macro `Value` (in Nightly builds, run with -Z macro-backtrace for more info)
|
32
crates/fayalite/tests/value_derive.rs
Normal file
32
crates/fayalite/tests/value_derive.rs
Normal file
|
@ -0,0 +1,32 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
// See Notices.txt for copyright information
|
||||
use fayalite::{
|
||||
int::UInt,
|
||||
ty::{StaticValue, Value},
|
||||
};
|
||||
|
||||
#[derive(Value, Clone, Hash, PartialEq, Eq, Debug)]
|
||||
#[hdl(outline_generated)]
|
||||
pub struct S<T> {
|
||||
pub a: T,
|
||||
b: UInt<3>,
|
||||
}
|
||||
|
||||
#[derive(Value, Clone, Hash, PartialEq, Eq, Debug)]
|
||||
#[hdl(outline_generated)]
|
||||
pub enum E<T> {
|
||||
A,
|
||||
B {},
|
||||
C(),
|
||||
D(UInt<3>),
|
||||
E { a: UInt<3> },
|
||||
F(UInt<3>, UInt<3>),
|
||||
G(T),
|
||||
H(T, UInt<1>),
|
||||
}
|
||||
|
||||
#[derive(Value, Clone, Hash, PartialEq, Eq, Debug)]
|
||||
#[hdl(outline_generated, static, where(T: StaticValue))]
|
||||
pub struct S2<T> {
|
||||
pub v: E<T>,
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,74 +0,0 @@
|
|||
#!/bin/bash
|
||||
# SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
# See Notices.txt for copyright information
|
||||
set -e
|
||||
|
||||
function fail()
|
||||
{
|
||||
local error="$1"
|
||||
echo "error: $error" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
function fail_file()
|
||||
{
|
||||
local file="$1" line="$2" error="$3"
|
||||
fail "$file:$((line + 1)): $error"
|
||||
}
|
||||
|
||||
function check_file()
|
||||
{
|
||||
local file="$1" regexes=("${@:2}")
|
||||
local lines
|
||||
mapfile -t lines < "$file"
|
||||
local line
|
||||
for line in "${!regexes[@]}"; do
|
||||
eval '[[ "${lines[i]}" =~ '"${regexes[i]}"' ]]' ||
|
||||
fail_file "$file" "$line" "doesn't match regex: ${regexes[i]}"
|
||||
done
|
||||
}
|
||||
|
||||
POUND_HEADER=('^"# SPDX-License-Identifier: LGPL-3.0-or-later"$' '^"# See Notices.txt for copyright information"$')
|
||||
SLASH_HEADER=('^"// SPDX-License-Identifier: LGPL-3.0-or-later"$' '^"// See Notices.txt for copyright information"$')
|
||||
MD_HEADER=('^"<!--"$' '^"SPDX-License-Identifier: LGPL-3.0-or-later"$' '^"See Notices.txt for copyright information"$')
|
||||
JSON_HEADER=('^"{"$' '^" \"license_header\": ["$' '^" \"SPDX-License-Identifier: LGPL-3.0-or-later\","$' '^" \"See Notices.txt for copyright information\""')
|
||||
|
||||
function main()
|
||||
{
|
||||
local IFS=$'\n'
|
||||
[[ -z "$(git status --porcelain)" ]] || fail "git repo is dirty"
|
||||
local file
|
||||
for file in $(git ls-tree --name-only --full-tree -r HEAD); do
|
||||
case "/$file" in
|
||||
/Cargo.lock)
|
||||
# generated file
|
||||
;;
|
||||
*/LICENSE.md|*/Notices.txt)
|
||||
# copyright file
|
||||
;;
|
||||
/crates/fayalite/tests/ui/*.stderr)
|
||||
# file that can't contain copyright header
|
||||
;;
|
||||
/.forgejo/workflows/*.yml|*/.gitignore|*.toml)
|
||||
check_file "$file" "${POUND_HEADER[@]}"
|
||||
;;
|
||||
*.md)
|
||||
check_file "$file" "${MD_HEADER[@]}"
|
||||
;;
|
||||
*.sh)
|
||||
check_file "$file" '^'\''#!'\' "${POUND_HEADER[@]}"
|
||||
;;
|
||||
*.rs)
|
||||
check_file "$file" "${SLASH_HEADER[@]}"
|
||||
;;
|
||||
*.json)
|
||||
check_file "$file" "${JSON_HEADER[@]}"
|
||||
;;
|
||||
*)
|
||||
fail_file "$file" 0 "unimplemented file kind -- you need to add it to $0"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
main
|
Loading…
Reference in a new issue