Compare commits

...

47 Commits

Author SHA1 Message Date
100e441a44 f 2022-04-05 15:28:21 +08:00
0a2dfab9a1 nac3core: add bound check for list slice 2022-04-05 14:29:40 +08:00
0fb9998a96 nac3core: add location information to codegen context 2022-04-05 14:29:40 +08:00
e05b0bf5dc flake: update nixpkgs 2022-04-05 10:10:08 +08:00
8eda0affc9 windows: add wine-msys2-build 2022-04-05 10:06:36 +08:00
75c53b40a3 windows: update msys2 packages, add setuptools to environment 2022-04-05 10:06:14 +08:00
0d10044d66 Merge pull request 'Fix float**int with negative power' (#254) from neg_powi_fix into master
Reviewed-on: M-Labs/nac3#254
2022-04-04 22:43:20 +08:00
23b7f4ef18 nac3standalone: add tests for power 2022-04-04 22:10:56 +08:00
710904f975 nac3core: fix powi with negative integer power 2022-04-04 22:10:56 +08:00
4bf452ec5a windows: do not check dependencies when making package 2022-04-04 16:03:59 +08:00
9fdce11efe windows: depend on python 2022-04-04 15:21:34 +08:00
f24ef85aed hydra: use msys2 type 2022-04-04 15:03:53 +08:00
4a19787f10 README: update 2022-04-04 15:03:44 +08:00
8209c0a475 windows: create MSYS2 package 2022-04-04 14:24:47 +08:00
4f66bdeda9 Merge pull request 'nac3core: do not get llvm value too eagerly for kernel invariant' (#253) from kernel_invariant_fix into master
Reviewed-on: M-Labs/nac3#253
2022-03-31 12:48:49 +08:00
57369896d7 update dependencies 2022-03-31 10:40:18 +08:00
2edeb31d21 nac3core: do not get llvm value too eagerly for kernel invariant 2022-03-31 10:28:16 +08:00
b8ef44d64e nac3standalone: add test for default param 2022-03-30 04:05:47 +08:00
c3156afebd nac3core: fix broken tests 2022-03-30 04:05:47 +08:00
388c9b7241 nac3core: better check and err msg for default param 2022-03-30 04:05:47 +08:00
e52d7fc97a nac3artiq: resolve unsigned int host variable as defautl param 2022-03-30 04:05:47 +08:00
6ab73a223c nac3core/artiq: support default param of option type 2022-03-30 04:05:47 +08:00
a38cc04444 nac3core: assert statement 2022-03-29 06:56:40 +08:00
1f5826d352 fix ternary if (#250)
Use store and load to handle if expression as the blocks might be changed when generating sub-expressions.

Reviewed-on: M-Labs/nac3#250
Co-authored-by: ychenfo <yc@m-labs.hk>
Co-committed-by: ychenfo <yc@m-labs.hk>
2022-03-29 06:54:00 +08:00
94eebde4ea README: add note about MSVC Python 2022-03-28 10:45:01 +08:00
63ec382673 README: update Windows instructions 2022-03-27 19:36:02 +08:00
0ca1a7bedb windows: work around broken LLD install script 2022-03-27 19:14:02 +08:00
201ca3f63d Revert "nac3artiq: use lld.exe on Windows"
This reverts commit 19182759cd.
2022-03-27 19:09:11 +08:00
19182759cd nac3artiq: use lld.exe on Windows 2022-03-27 18:41:38 +08:00
edd039abdc windows: build LLD 2022-03-27 18:41:23 +08:00
3852cc1058 windows: don't fixup LLVM 2022-03-27 18:38:23 +08:00
0600ee8efa nac3artiq: use correct lld invokation on Windows 2022-03-27 18:25:14 +08:00
bed33a7421 nac3standalone: add tests for tuple 2022-03-27 10:31:20 +08:00
0d2b844a2e nac3artiq: avoid getting tuple as pointer value 2022-03-27 10:31:20 +08:00
8d7e300a4a nac3core: do not use const struct for tuple 2022-03-27 10:13:17 +08:00
10d623e36f nac3core/artiq: fix tuple representation 2022-03-27 07:47:14 +08:00
000b128551 nac3artiq: cast none to correct ptr type (#241) 2022-03-26 23:32:50 +08:00
e4581a6d9b nac3standalone/demo: fix return type in loop.py 2022-03-26 21:10:12 +08:00
1a82d296e7 nac3core/codegen: prevent users from modifying loop counter
Fixes #211
2022-03-26 20:58:37 +08:00
bf067e2481 nac3artiq: implement attribute writeback
We will only writeback attributes that are supported by the current RPC
implementation: primitives, tuple and lists of lists... of primitives.
2022-03-26 20:13:58 +08:00
ba8ed6c663 nac3artiq: handle recursive types properly 2022-03-26 18:54:21 +08:00
26a4834254 fix warnings 2022-03-26 18:52:08 +08:00
1ad4b0227c windows: fix src location 2022-03-26 15:46:21 +08:00
6288a66dc5 windows: fix cargo lockfile location 2022-03-26 15:23:31 +08:00
de4320eefb improve package names 2022-03-26 15:15:59 +08:00
a380cd5010 move all Nix files to one folder 2022-03-26 15:13:43 +08:00
80631fc92b Option type support (#224)
Co-authored-by: ychenfo <yc@m-labs.hk>
Co-committed-by: ychenfo <yc@m-labs.hk>
2022-03-26 15:09:15 +08:00
49 changed files with 1597 additions and 371 deletions

117
Cargo.lock generated
View File

@ -249,9 +249,9 @@ dependencies = [
[[package]] [[package]]
name = "getrandom" name = "getrandom"
version = "0.2.5" version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77" checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
@ -278,9 +278,9 @@ dependencies = [
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "1.8.0" version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223" checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
dependencies = [ dependencies = [
"autocfg", "autocfg",
"hashbrown", "hashbrown",
@ -329,7 +329,7 @@ dependencies = [
"libc", "libc",
"llvm-sys", "llvm-sys",
"once_cell", "once_cell",
"parking_lot", "parking_lot 0.11.2",
"regex", "regex",
] ]
@ -457,10 +457,11 @@ dependencies = [
[[package]] [[package]]
name = "lock_api" name = "lock_api"
version = "0.4.6" version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
dependencies = [ dependencies = [
"autocfg",
"scopeguard", "scopeguard",
] ]
@ -495,7 +496,7 @@ dependencies = [
"inkwell", "inkwell",
"nac3core", "nac3core",
"nac3parser", "nac3parser",
"parking_lot", "parking_lot 0.11.2",
"pyo3", "pyo3",
"tempfile", "tempfile",
] ]
@ -506,7 +507,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"fxhash", "fxhash",
"lazy_static", "lazy_static",
"parking_lot", "parking_lot 0.11.2",
"string-interner", "string-interner",
] ]
@ -520,7 +521,7 @@ dependencies = [
"insta", "insta",
"itertools", "itertools",
"nac3parser", "nac3parser",
"parking_lot", "parking_lot 0.11.2",
"rayon", "rayon",
"regex", "regex",
"test-case", "test-case",
@ -549,7 +550,7 @@ dependencies = [
"inkwell", "inkwell",
"nac3core", "nac3core",
"nac3parser", "nac3parser",
"parking_lot", "parking_lot 0.11.2",
] ]
[[package]] [[package]]
@ -582,7 +583,17 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
dependencies = [ dependencies = [
"instant", "instant",
"lock_api", "lock_api",
"parking_lot_core", "parking_lot_core 0.8.5",
]
[[package]]
name = "parking_lot"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87f5ec2493a61ac0506c0f4199f99070cbe83857b0337006a30f3e6719b8ef58"
dependencies = [
"lock_api",
"parking_lot_core 0.9.2",
] ]
[[package]] [[package]]
@ -599,6 +610,19 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "parking_lot_core"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "995f667a6c822200b0433ac218e05582f0e2efa1b922a3fd2fbaadc5f87bab37"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-sys",
]
[[package]] [[package]]
name = "paste" name = "paste"
version = "0.1.18" version = "0.1.18"
@ -732,7 +756,7 @@ dependencies = [
"cfg-if", "cfg-if",
"indoc 0.3.6", "indoc 0.3.6",
"libc", "libc",
"parking_lot", "parking_lot 0.11.2",
"paste", "paste",
"pyo3-build-config", "pyo3-build-config",
"pyo3-macros", "pyo3-macros",
@ -773,9 +797,9 @@ dependencies = [
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.16" version = "1.0.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4af2ec4714533fcdf07e886f17025ace8b997b9ce51204ee69b6da831c3da57" checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -837,18 +861,18 @@ dependencies = [
[[package]] [[package]]
name = "redox_syscall" name = "redox_syscall"
version = "0.2.11" version = "0.2.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8380fe0152551244f0747b1bf41737e0f8a74f97a14ccefd1148187271634f3c" checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
dependencies = [ dependencies = [
"bitflags", "bitflags",
] ]
[[package]] [[package]]
name = "redox_users" name = "redox_users"
version = "0.4.2" version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7776223e2696f1aa4c6b0170e83212f47296a00424305117d013dfe86fb0fe55" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
dependencies = [ dependencies = [
"getrandom", "getrandom",
"redox_syscall", "redox_syscall",
@ -998,22 +1022,22 @@ dependencies = [
[[package]] [[package]]
name = "string_cache" name = "string_cache"
version = "0.8.3" version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33994d0838dc2d152d17a62adf608a869b5e846b65b389af7f3dbc1de45c5b26" checksum = "213494b7a2b503146286049378ce02b482200519accc31872ee8be91fa820a08"
dependencies = [ dependencies = [
"lazy_static",
"new_debug_unreachable", "new_debug_unreachable",
"parking_lot", "once_cell",
"parking_lot 0.12.0",
"phf_shared 0.10.0", "phf_shared 0.10.0",
"precomputed-hash", "precomputed-hash",
] ]
[[package]] [[package]]
name = "syn" name = "syn"
version = "1.0.89" version = "1.0.90"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea297be220d52398dcc07ce15a209fce436d361735ac1db700cab3b6cdfb9f54" checksum = "704df27628939572cd88d33f171cd6f896f4eaca85252c6e0a72d8d8287ee86f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -1207,6 +1231,49 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5acdd78cb4ba54c0045ac14f62d8f94a03d10047904ae2a40afa1e99d8f70825"
dependencies = [
"windows_aarch64_msvc",
"windows_i686_gnu",
"windows_i686_msvc",
"windows_x86_64_gnu",
"windows_x86_64_msvc",
]
[[package]]
name = "windows_aarch64_msvc"
version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "17cffbe740121affb56fad0fc0e421804adf0ae00891205213b5cecd30db881d"
[[package]]
name = "windows_i686_gnu"
version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2564fde759adb79129d9b4f54be42b32c89970c18ebf93124ca8870a498688ed"
[[package]]
name = "windows_i686_msvc"
version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cd9d32ba70453522332c14d38814bceeb747d80b3958676007acadd7e166956"
[[package]]
name = "windows_x86_64_gnu"
version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfce6deae227ee8d356d19effc141a509cc503dfd1f850622ec4b0f84428e1f4"
[[package]]
name = "windows_x86_64_msvc"
version = "0.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d19538ccc21819d01deaf88d6a17eae6596a12e9aafdbb97916fb49896d89de9"
[[package]] [[package]]
name = "yaml-rust" name = "yaml-rust"
version = "0.4.5" version = "0.4.5"

View File

@ -37,20 +37,22 @@ cd artiq
python setup.py install python setup.py install
``` ```
Locate a recent build of ``nac3artiq-msys2`` from [Hydra](https://nixbld.m-labs.hk) and download ``nac3artiq.zip``. Then extract the contents in the appropriate location: Locate a recent build of ``nac3artiq-msys2-pkg`` from [Hydra](https://nixbld.m-labs.hk), download the package, and install it manually with pacman:
``` ```
pacman -S unzip wget https://nixbld.m-labs.hk/build/116315/download/1/mingw-w64-x86_64-nac3artiq-1.0-1-any.pkg.tar.zst # edit the build number
wget https://nixbld.m-labs.hk/build/97899/download/1/nac3artiq.zip # edit the build number pacman -U mingw-w64-x86_64-nac3artiq-1.0-1-any.pkg.tar.zst
unzip nac3artiq.zip -d C:/msys64/mingw64/lib/python3.9/site-packages
``` ```
Install additional NAC3 dependencies: Install ``lld-msys2`` manually:
``` ```
pacman -S mingw-w64-x86_64-lld wget https://nixbld.m-labs.hk/build/115527/download/1/ld.lld.exe
mv ld.lld.exe C:/msys64/mingw64/bin
``` ```
And you should be good to go. And you should be good to go.
Note: This build of NAC3 cannot be used with Anaconda Python nor the python.org binaries for Windows. Those Python versions are compiled with Visual Studio (MSVC) and their ABI is incompatible with the GNU ABI used in this build. We have no plans to support Visual Studio nor the MSVC ABI. If you need a MSVC build, please install the requisite bloated spyware from Microsoft and compile NAC3 yourself.
## For developers ## For developers
This repository contains: This repository contains:

6
flake.lock generated
View File

@ -2,11 +2,11 @@
"nodes": { "nodes": {
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1647992509, "lastModified": 1649024309,
"narHash": "sha256-AG40Nt5OWz0LBs5p457emOuwLKOvTtcv/2fUdnEN3Ws=", "narHash": "sha256-AWbvj/NHZXVwAnHaVOFlxg7tcNerEKrKBmgGfztSHWM=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "d2caa9377539e3b5ff1272ac3aa2d15f3081069f", "rev": "af0a9bc0e5341855518e9c1734d7ef913e5138b9",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@ -8,7 +8,7 @@
pkgs = import nixpkgs { system = "x86_64-linux"; }; pkgs = import nixpkgs { system = "x86_64-linux"; };
in rec { in rec {
packages.x86_64-linux = rec { packages.x86_64-linux = rec {
llvm-nac3 = pkgs.callPackage "${self}/llvm" {}; llvm-nac3 = pkgs.callPackage ./nix/llvm {};
nac3artiq = pkgs.python3Packages.toPythonModule ( nac3artiq = pkgs.python3Packages.toPythonModule (
pkgs.rustPlatform.buildRustPackage { pkgs.rustPlatform.buildRustPackage {
name = "nac3artiq"; name = "nac3artiq";
@ -48,7 +48,7 @@
}; };
# LLVM PGO support # LLVM PGO support
llvm-nac3-instrumented = pkgs.callPackage "${self}/llvm" { llvm-nac3-instrumented = pkgs.callPackage ./nix/llvm {
stdenv = pkgs.llvmPackages_13.stdenv; stdenv = pkgs.llvmPackages_13.stdenv;
extraCmakeFlags = [ "-DLLVM_BUILD_INSTRUMENTED=IR" ]; extraCmakeFlags = [ "-DLLVM_BUILD_INSTRUMENTED=IR" ];
}; };
@ -86,7 +86,7 @@
llvm-profdata merge -o $out/llvm.profdata /build/llvm/build/profiles/* llvm-profdata merge -o $out/llvm.profdata /build/llvm/build/profiles/*
''; '';
}; };
llvm-nac3-pgo = pkgs.callPackage "${self}/llvm" { llvm-nac3-pgo = pkgs.callPackage ./nix/llvm {
stdenv = pkgs.llvmPackages_13.stdenv; stdenv = pkgs.llvmPackages_13.stdenv;
extraCmakeFlags = [ "-DLLVM_PROFDATA_FILE=${nac3artiq-profile}/llvm.profdata" ]; extraCmakeFlags = [ "-DLLVM_PROFDATA_FILE=${nac3artiq-profile}/llvm.profdata" ];
}; };
@ -109,7 +109,7 @@
); );
}; };
packages.x86_64-w64-mingw32 = import ./windows { inherit pkgs; }; packages.x86_64-w64-mingw32 = import ./nix/windows { inherit pkgs; };
devShell.x86_64-linux = pkgs.mkShell { devShell.x86_64-linux = pkgs.mkShell {
name = "nac3-dev-shell"; name = "nac3-dev-shell";
@ -129,7 +129,7 @@
]; ];
}; };
devShells.x86_64-linux.msys2 = pkgs.mkShell { devShells.x86_64-linux.msys2 = pkgs.mkShell {
name = "nac3-dev-shell"; name = "nac3-dev-shell-msys2";
buildInputs = with pkgs; [ buildInputs = with pkgs; [
curl curl
pacman pacman
@ -142,6 +142,8 @@
inherit (packages.x86_64-linux) llvm-nac3 nac3artiq; inherit (packages.x86_64-linux) llvm-nac3 nac3artiq;
llvm-nac3-msys2 = packages.x86_64-w64-mingw32.llvm-nac3; llvm-nac3-msys2 = packages.x86_64-w64-mingw32.llvm-nac3;
nac3artiq-msys2 = packages.x86_64-w64-mingw32.nac3artiq; nac3artiq-msys2 = packages.x86_64-w64-mingw32.nac3artiq;
nac3artiq-msys2-pkg = packages.x86_64-w64-mingw32.nac3artiq-pkg;
lld-msys2 = packages.x86_64-w64-mingw32.lld;
}; };
}; };

View File

@ -5,6 +5,7 @@ class EmbeddingMap:
self.string_map = {} self.string_map = {}
self.string_reverse_map = {} self.string_reverse_map = {}
self.function_map = {} self.function_map = {}
self.attributes_writeback = []
# preallocate exception names # preallocate exception names
self.preallocate_runtime_exception_names(["RuntimeError", self.preallocate_runtime_exception_names(["RuntimeError",
@ -16,7 +17,8 @@ class EmbeddingMap:
"CacheError", "CacheError",
"SPIError", "SPIError",
"0:ZeroDivisionError", "0:ZeroDivisionError",
"0:IndexError"]) "0:IndexError",
"0:UnwrapNoneError"])
def preallocate_runtime_exception_names(self, names): def preallocate_runtime_exception_names(self, names):
for i, name in enumerate(names): for i, name in enumerate(names):

View File

@ -11,6 +11,7 @@ from embedding_map import EmbeddingMap
__all__ = [ __all__ = [
"Kernel", "KernelInvariant", "virtual", "Kernel", "KernelInvariant", "virtual",
"Option", "Some", "none", "UnwrapNoneError",
"round64", "floor64", "ceil64", "round64", "floor64", "ceil64",
"extern", "kernel", "portable", "nac3", "extern", "kernel", "portable", "nac3",
"rpc", "ms", "us", "ns", "rpc", "ms", "us", "ns",
@ -32,6 +33,39 @@ class KernelInvariant(Generic[T]):
class virtual(Generic[T]): class virtual(Generic[T]):
pass pass
class Option(Generic[T]):
_nac3_option: T
def __init__(self, v: T):
self._nac3_option = v
def is_none(self):
return self._nac3_option is None
def is_some(self):
return not self.is_none()
def unwrap(self):
if self.is_none():
raise UnwrapNoneError()
return self._nac3_option
def __repr__(self) -> str:
if self.is_none():
return "none"
else:
return "Some({})".format(repr(self._nac3_option))
def __str__(self) -> str:
if self.is_none():
return "none"
else:
return "Some({})".format(str(self._nac3_option))
def Some(v: T) -> Option[T]:
return Option(v)
none = Option(None)
def round64(x): def round64(x):
return round(x) return round(x)
@ -240,5 +274,10 @@ class KernelContextManager:
def __exit__(self): def __exit__(self):
pass pass
@nac3
class UnwrapNoneError(Exception):
"""raised when unwrapping a none value"""
artiq_builtin = True
parallel = KernelContextManager() parallel = KernelContextManager()
sequential = KernelContextManager() sequential = KernelContextManager()

View File

@ -6,7 +6,7 @@ use nac3core::{
}, },
symbol_resolver::ValueEnum, symbol_resolver::ValueEnum,
toplevel::{DefinitionId, GenCall}, toplevel::{DefinitionId, GenCall},
typecheck::typedef::{FunSignature, Type}, typecheck::typedef::{FunSignature, FuncArg, Type, TypeEnum}
}; };
use nac3parser::ast::{Expr, ExprKind, Located, Stmt, StmtKind, StrRef}; use nac3parser::ast::{Expr, ExprKind, Located, Stmt, StmtKind, StrRef};
@ -15,7 +15,9 @@ use inkwell::{
context::Context, module::Linkage, types::IntType, values::BasicValueEnum, AddressSpace, context::Context, module::Linkage, types::IntType, values::BasicValueEnum, AddressSpace,
}; };
use crate::timeline::TimeFns; use pyo3::{PyObject, PyResult, Python, types::{PyDict, PyList}};
use crate::{symbol_resolver::InnerResolver, timeline::TimeFns};
use std::{ use std::{
collections::hash_map::DefaultHasher, collections::hash_map::DefaultHasher,
@ -270,8 +272,6 @@ fn gen_rpc_tag<'ctx, 'a>(
buffer.push(b'l'); buffer.push(b'l');
gen_rpc_tag(ctx, *ty, buffer)?; gen_rpc_tag(ctx, *ty, buffer)?;
} }
// we should return an error, this will be fixed after improving error message
// as this requires returning an error during codegen
_ => return Err(format!("Unsupported type: {:?}", ctx.unifier.stringify(ty))), _ => return Err(format!("Unsupported type: {:?}", ctx.unifier.stringify(ty))),
} }
} }
@ -291,7 +291,7 @@ fn rpc_codegen_callback_fn<'ctx, 'a>(
let int32 = ctx.ctx.i32_type(); let int32 = ctx.ctx.i32_type();
let tag_ptr_type = ctx.ctx.struct_type(&[ptr_type.into(), size_type.into()], false); let tag_ptr_type = ctx.ctx.struct_type(&[ptr_type.into(), size_type.into()], false);
let service_id = int32.const_int(fun.1 .0 as u64, false); let service_id = int32.const_int(fun.1.0 as u64, false);
// -- setup rpc tags // -- setup rpc tags
let mut tag = Vec::new(); let mut tag = Vec::new();
if obj.is_some() { if obj.is_some() {
@ -361,7 +361,10 @@ fn rpc_codegen_callback_fn<'ctx, 'a>(
} }
// default value handling // default value handling
for k in keys.into_iter() { for k in keys.into_iter() {
mapping.insert(k.name, ctx.gen_symbol_val(generator, &k.default_value.unwrap()).into()); mapping.insert(
k.name,
ctx.gen_symbol_val(generator, &k.default_value.unwrap(), k.ty).into()
);
} }
// reorder the parameters // reorder the parameters
let mut real_params = fun let mut real_params = fun
@ -486,6 +489,81 @@ fn rpc_codegen_callback_fn<'ctx, 'a>(
Ok(Some(result)) Ok(Some(result))
} }
pub fn attributes_writeback<'ctx, 'a>(
ctx: &mut CodeGenContext<'ctx, 'a>,
generator: &mut dyn CodeGenerator,
inner_resolver: &InnerResolver,
host_attributes: PyObject,
) -> Result<(), String> {
Python::with_gil(|py| -> PyResult<Result<(), String>> {
let host_attributes = host_attributes.cast_as::<PyList>(py)?;
let top_levels = ctx.top_level.definitions.read();
let globals = inner_resolver.global_value_ids.read();
let int32 = ctx.ctx.i32_type();
let zero = int32.const_zero();
let mut values = Vec::new();
let mut scratch_buffer = Vec::new();
for (_, val) in globals.iter() {
let val = val.as_ref(py);
let ty = inner_resolver.get_obj_type(py, val, &mut ctx.unifier, &top_levels, &ctx.primitives)?;
if let Err(ty) = ty {
return Ok(Err(ty))
}
let ty = ty.unwrap();
match &*ctx.unifier.get_ty(ty) {
TypeEnum::TObj { fields, .. } => {
// we only care about primitive attributes
// for non-primitive attributes, they should be in another global
let mut attributes = Vec::new();
let obj = inner_resolver.get_obj_value(py, val, ctx, generator)?.unwrap();
for (name, (field_ty, is_mutable)) in fields.iter() {
if !is_mutable {
continue
}
if gen_rpc_tag(ctx, *field_ty, &mut scratch_buffer).is_ok() {
attributes.push(name.to_string());
let index = ctx.get_attr_index(ty, *name);
values.push((*field_ty, ctx.build_gep_and_load(
obj.into_pointer_value(),
&[zero, int32.const_int(index as u64, false)])));
}
}
if !attributes.is_empty() {
let pydict = PyDict::new(py);
pydict.set_item("obj", val)?;
pydict.set_item("fields", attributes)?;
host_attributes.append(pydict)?;
}
},
TypeEnum::TList { ty: elem_ty } => {
if gen_rpc_tag(ctx, *elem_ty, &mut scratch_buffer).is_ok() {
let pydict = PyDict::new(py);
pydict.set_item("obj", val)?;
host_attributes.append(pydict)?;
values.push((ty, inner_resolver.get_obj_value(py, val, ctx, generator)?.unwrap()));
}
},
_ => {}
}
}
let fun = FunSignature {
args: values.iter().enumerate().map(|(i, (ty, _))| FuncArg {
name: i.to_string().into(),
ty: *ty,
default_value: None
}).collect(),
ret: ctx.primitives.none,
vars: Default::default()
};
let args: Vec<_> = values.into_iter().map(|(_, val)| (None, ValueEnum::Dynamic(val))).collect();
if let Err(e) = rpc_codegen_callback_fn(ctx, None, (&fun, DefinitionId(0)), args, generator) {
return Ok(Err(e));
}
Ok(Ok(()))
}).unwrap()?;
Ok(())
}
pub fn rpc_codegen_callback() -> Arc<GenCall> { pub fn rpc_codegen_callback() -> Arc<GenCall> {
Arc::new(GenCall::new(Box::new(|ctx, obj, fun, args, generator| { Arc::new(GenCall::new(Box::new(|ctx, obj, fun, args, generator| {
rpc_codegen_callback_fn(ctx, obj, fun, args, generator) rpc_codegen_callback_fn(ctx, obj, fun, args, generator)

View File

@ -10,6 +10,7 @@ use inkwell::{
targets::*, targets::*,
OptimizationLevel, OptimizationLevel,
}; };
use nac3core::codegen::gen_func_impl;
use nac3core::toplevel::builtins::get_exn_constructor; use nac3core::toplevel::builtins::get_exn_constructor;
use nac3core::typecheck::typedef::{TypeEnum, Unifier}; use nac3core::typecheck::typedef::{TypeEnum, Unifier};
use nac3parser::{ use nac3parser::{
@ -36,6 +37,7 @@ use nac3core::{
use tempfile::{self, TempDir}; use tempfile::{self, TempDir};
use crate::codegen::attributes_writeback;
use crate::{ use crate::{
codegen::{rpc_codegen_callback, ArtiqCodeGenerator}, codegen::{rpc_codegen_callback, ArtiqCodeGenerator},
symbol_resolver::{InnerResolver, PythonHelper, Resolver, DeferredEvaluationStore}, symbol_resolver::{InnerResolver, PythonHelper, Resolver, DeferredEvaluationStore},
@ -71,6 +73,7 @@ pub struct PrimitivePythonId {
exception: u64, exception: u64,
generic_alias: (u64, u64), generic_alias: (u64, u64),
virtual_id: u64, virtual_id: u64,
option: u64,
} }
type TopLevelComponent = (Stmt, String, PyObject); type TopLevelComponent = (Stmt, String, PyObject);
@ -373,7 +376,17 @@ impl Nac3 {
get_attr_id(typing_mod, "_GenericAlias"), get_attr_id(typing_mod, "_GenericAlias"),
get_attr_id(types_mod, "GenericAlias"), get_attr_id(types_mod, "GenericAlias"),
), ),
none: get_attr_id(builtins_mod, "None"), none: id_fn
.call1((builtins_mod
.getattr("globals")
.unwrap()
.call0()
.unwrap()
.get_item("none")
.unwrap(),))
.unwrap()
.extract()
.unwrap(),
typevar: get_attr_id(typing_mod, "TypeVar"), typevar: get_attr_id(typing_mod, "TypeVar"),
int: get_attr_id(builtins_mod, "int"), int: get_attr_id(builtins_mod, "int"),
int32: get_attr_id(numpy_mod, "int32"), int32: get_attr_id(numpy_mod, "int32"),
@ -385,6 +398,17 @@ impl Nac3 {
list: get_attr_id(builtins_mod, "list"), list: get_attr_id(builtins_mod, "list"),
tuple: get_attr_id(builtins_mod, "tuple"), tuple: get_attr_id(builtins_mod, "tuple"),
exception: get_attr_id(builtins_mod, "Exception"), exception: get_attr_id(builtins_mod, "Exception"),
option: id_fn
.call1((builtins_mod
.getattr("globals")
.unwrap()
.call0()
.unwrap()
.get_item("Option")
.unwrap(),))
.unwrap()
.extract()
.unwrap(),
}; };
let working_directory = tempfile::Builder::new().prefix("nac3-").tempdir().unwrap(); let working_directory = tempfile::Builder::new().prefix("nac3-").tempdir().unwrap();
@ -454,6 +478,8 @@ impl Nac3 {
let store_obj = embedding_map.getattr("store_object").unwrap().to_object(py); let store_obj = embedding_map.getattr("store_object").unwrap().to_object(py);
let store_str = embedding_map.getattr("store_str").unwrap().to_object(py); let store_str = embedding_map.getattr("store_str").unwrap().to_object(py);
let store_fun = embedding_map.getattr("store_function").unwrap().to_object(py); let store_fun = embedding_map.getattr("store_function").unwrap().to_object(py);
let host_attributes = embedding_map.getattr("attributes_writeback").unwrap().to_object(py);
let global_value_ids: Arc<RwLock<HashMap<_, _>>> = Arc::new(RwLock::new(HashMap::new()));
let helper = PythonHelper { let helper = PythonHelper {
id_fn: builtins.getattr("id").unwrap().to_object(py), id_fn: builtins.getattr("id").unwrap().to_object(py),
len_fn: builtins.getattr("len").unwrap().to_object(py), len_fn: builtins.getattr("len").unwrap().to_object(py),
@ -474,13 +500,13 @@ impl Nac3 {
"KeyError", "KeyError",
"NotImplementedError", "NotImplementedError",
"OverflowError", "OverflowError",
"IOError" "IOError",
"UnwrapNoneError",
]; ];
add_exceptions(&mut composer, &mut builtins_def, &mut builtins_ty, &exception_names); add_exceptions(&mut composer, &mut builtins_def, &mut builtins_ty, &exception_names);
let mut module_to_resolver_cache: HashMap<u64, _> = HashMap::new(); let mut module_to_resolver_cache: HashMap<u64, _> = HashMap::new();
let global_value_ids = Arc::new(RwLock::new(HashSet::<u64>::new()));
let mut rpc_ids = vec![]; let mut rpc_ids = vec![];
for (stmt, path, module) in self.top_levels.iter() { for (stmt, path, module) in self.top_levels.iter() {
let py_module: &PyAny = module.extract(py)?; let py_module: &PyAny = module.extract(py)?;
@ -594,7 +620,7 @@ impl Nac3 {
}; };
let mut synthesized = let mut synthesized =
parse_program(&synthesized, "__nac3_synthesized_modinit__".to_string().into()).unwrap(); parse_program(&synthesized, "__nac3_synthesized_modinit__".to_string().into()).unwrap();
let resolver = Arc::new(Resolver(Arc::new(InnerResolver { let inner_resolver = Arc::new(InnerResolver {
id_to_type: builtins_ty.clone().into(), id_to_type: builtins_ty.clone().into(),
id_to_def: builtins_def.clone().into(), id_to_def: builtins_def.clone().into(),
pyid_to_def: self.pyid_to_def.clone(), pyid_to_def: self.pyid_to_def.clone(),
@ -611,17 +637,18 @@ impl Nac3 {
string_store: self.string_store.clone(), string_store: self.string_store.clone(),
exception_ids: self.exception_ids.clone(), exception_ids: self.exception_ids.clone(),
deferred_eval_store: self.deferred_eval_store.clone(), deferred_eval_store: self.deferred_eval_store.clone(),
}))) as Arc<dyn SymbolResolver + Send + Sync>; });
let resolver = Arc::new(Resolver(inner_resolver.clone())) as Arc<dyn SymbolResolver + Send + Sync>;
let (_, def_id, _) = composer let (_, def_id, _) = composer
.register_top_level(synthesized.pop().unwrap(), Some(resolver.clone()), "".into()) .register_top_level(synthesized.pop().unwrap(), Some(resolver.clone()), "".into())
.unwrap(); .unwrap();
let signature = let fun_signature =
FunSignature { args: vec![], ret: self.primitive.none, vars: HashMap::new() }; FunSignature { args: vec![], ret: self.primitive.none, vars: HashMap::new() };
let mut store = ConcreteTypeStore::new(); let mut store = ConcreteTypeStore::new();
let mut cache = HashMap::new(); let mut cache = HashMap::new();
let signature = let signature =
store.from_signature(&mut composer.unifier, &self.primitive, &signature, &mut cache); store.from_signature(&mut composer.unifier, &self.primitive, &fun_signature, &mut cache);
let signature = store.add_cty(signature); let signature = store.add_cty(signature);
if let Err(e) = composer.start_analysis(true) { if let Err(e) = composer.start_analysis(true) {
@ -698,12 +725,29 @@ impl Nac3 {
symbol_name: "__modinit__".to_string(), symbol_name: "__modinit__".to_string(),
body: instance.body, body: instance.body,
signature, signature,
resolver, resolver: resolver.clone(),
store, store,
unifier_index: instance.unifier_id, unifier_index: instance.unifier_id,
calls: instance.calls, calls: instance.calls,
id: 0, id: 0,
}; };
let mut store = ConcreteTypeStore::new();
let mut cache = HashMap::new();
let signature =
store.from_signature(&mut composer.unifier, &self.primitive, &fun_signature, &mut cache);
let signature = store.add_cty(signature);
let attributes_writeback_task = CodeGenTask {
subst: Default::default(),
symbol_name: "attributes_writeback".to_string(),
body: Arc::new(Default::default()),
signature,
resolver,
store,
unifier_index: instance.unifier_id,
calls: Arc::new(Default::default()),
id: 0,
};
let isa = self.isa; let isa = self.isa;
let working_directory = self.working_directory.path().to_owned(); let working_directory = self.working_directory.path().to_owned();
@ -723,14 +767,27 @@ impl Nac3 {
.map(|s| Box::new(ArtiqCodeGenerator::new(s.to_string(), size_t, self.time_fns))) .map(|s| Box::new(ArtiqCodeGenerator::new(s.to_string(), size_t, self.time_fns)))
.collect(); .collect();
let membuffer = membuffers.clone();
py.allow_threads(|| { py.allow_threads(|| {
let (registry, handles) = WorkerRegistry::create_workers(threads, top_level.clone(), f); let (registry, handles) = WorkerRegistry::create_workers(threads, top_level.clone(), f);
registry.add_task(task); registry.add_task(task);
registry.wait_tasks_complete(handles); registry.wait_tasks_complete(handles);
let mut generator = ArtiqCodeGenerator::new("attributes_writeback".to_string(), size_t, self.time_fns);
let context = inkwell::context::Context::create();
let module = context.create_module("attributes_writeback");
let builder = context.create_builder();
let (_, module, _) = gen_func_impl(&context, &mut generator, &registry, builder, module,
attributes_writeback_task, |generator, ctx| {
attributes_writeback(ctx, generator, inner_resolver.as_ref(), host_attributes)
}).unwrap();
let buffer = module.write_bitcode_to_memory();
let buffer = buffer.as_slice().into();
membuffer.lock().push(buffer);
}); });
let buffers = membuffers.lock();
let context = inkwell::context::Context::create(); let context = inkwell::context::Context::create();
let buffers = membuffers.lock();
let main = context let main = context
.create_module_from_ir(MemoryBuffer::create_from_memory_range(&buffers[0], "main")) .create_module_from_ir(MemoryBuffer::create_from_memory_range(&buffers[0], "main"))
.unwrap(); .unwrap();
@ -742,6 +799,11 @@ impl Nac3 {
main.link_in_module(other) main.link_in_module(other)
.map_err(|err| CompileError::new_err(err.to_string()))?; .map_err(|err| CompileError::new_err(err.to_string()))?;
} }
let builder = context.create_builder();
let modinit_return = main.get_function("__modinit__").unwrap().get_last_basic_block().unwrap().get_terminator().unwrap();
builder.position_before(&modinit_return);
builder.build_call(main.get_function("attributes_writeback").unwrap(), &[], "attributes_writeback");
main.link_in_module(load_irrt(&context)) main.link_in_module(load_irrt(&context))
.map_err(|err| CompileError::new_err(err.to_string()))?; .map_err(|err| CompileError::new_err(err.to_string()))?;
@ -805,7 +867,11 @@ impl Nac3 {
); );
} }
if let Ok(linker_status) = Command::new("ld.lld").args(linker_args).status() { #[cfg(not(windows))]
let lld_command = "ld.lld";
#[cfg(windows)]
let lld_command = "ld.lld.exe";
if let Ok(linker_status) = Command::new(lld_command).args(linker_args).status() {
if !linker_status.success() { if !linker_status.success() {
return Err(CompileError::new_err("failed to start linker")); return Err(CompileError::new_err("failed to start linker"));
} }

View File

@ -15,7 +15,7 @@ use pyo3::{
PyAny, PyObject, PyResult, Python, PyAny, PyObject, PyResult, Python,
}; };
use std::{ use std::{
collections::{HashMap, HashSet}, collections::HashMap,
sync::{ sync::{
Arc, Arc,
atomic::{AtomicBool, Ordering::Relaxed} atomic::{AtomicBool, Ordering::Relaxed}
@ -54,7 +54,7 @@ pub struct InnerResolver {
pub id_to_pyval: RwLock<HashMap<StrRef, (u64, PyObject)>>, pub id_to_pyval: RwLock<HashMap<StrRef, (u64, PyObject)>>,
pub id_to_primitive: RwLock<HashMap<u64, PrimitiveValue>>, pub id_to_primitive: RwLock<HashMap<u64, PrimitiveValue>>,
pub field_to_val: RwLock<HashMap<(u64, StrRef), Option<(u64, PyObject)>>>, pub field_to_val: RwLock<HashMap<(u64, StrRef), Option<(u64, PyObject)>>>,
pub global_value_ids: Arc<RwLock<HashSet<u64>>>, pub global_value_ids: Arc<RwLock<HashMap<u64, PyObject>>>,
pub class_names: Mutex<HashMap<StrRef, Type>>, pub class_names: Mutex<HashMap<StrRef, Type>>,
pub pyid_to_def: Arc<RwLock<HashMap<u64, DefinitionId>>>, pub pyid_to_def: Arc<RwLock<HashMap<u64, DefinitionId>>>,
pub pyid_to_type: Arc<RwLock<HashMap<u64, Type>>>, pub pyid_to_type: Arc<RwLock<HashMap<u64, Type>>>,
@ -279,6 +279,10 @@ impl InnerResolver {
} else if ty_id == self.primitive_ids.tuple { } else if ty_id == self.primitive_ids.tuple {
// do not handle type var param and concrete check here // do not handle type var param and concrete check here
Ok(Ok((unifier.add_ty(TypeEnum::TTuple { ty: vec![] }), false))) Ok(Ok((unifier.add_ty(TypeEnum::TTuple { ty: vec![] }), false)))
} else if ty_id == self.primitive_ids.option {
Ok(Ok((primitives.option, false)))
} else if ty_id == self.primitive_ids.none {
unreachable!("none cannot be typeid")
} else if let Some(def_id) = self.pyid_to_def.read().get(&ty_id).cloned() { } else if let Some(def_id) = self.pyid_to_def.read().get(&ty_id).cloned() {
let def = defs[def_id.0].read(); let def = defs[def_id.0].read();
if let TopLevelDef::Class { object_id, type_vars, fields, methods, .. } = &*def { if let TopLevelDef::Class { object_id, type_vars, fields, methods, .. } = &*def {
@ -499,7 +503,7 @@ impl InnerResolver {
} }
} }
fn get_obj_type( pub fn get_obj_type(
&self, &self,
py: Python, py: Python,
obj: &PyAny, obj: &PyAny,
@ -508,8 +512,8 @@ impl InnerResolver {
primitives: &PrimitiveStore, primitives: &PrimitiveStore,
) -> PyResult<Result<Type, String>> { ) -> PyResult<Result<Type, String>> {
let ty = self.helper.type_fn.call1(py, (obj,)).unwrap(); let ty = self.helper.type_fn.call1(py, (obj,)).unwrap();
let ty_id: u64 = self.helper.id_fn.call1(py, (ty.clone(),))?.extract(py)?; let py_obj_id: u64 = self.helper.id_fn.call1(py, (obj,))?.extract(py)?;
if let Some(ty) = self.pyid_to_type.read().get(&ty_id) { if let Some(ty) = self.pyid_to_type.read().get(&py_obj_id) {
return Ok(Ok(*ty)) return Ok(Ok(*ty))
} }
let (extracted_ty, inst_check) = match self.get_pyty_obj_type( let (extracted_ty, inst_check) = match self.get_pyty_obj_type(
@ -569,7 +573,54 @@ impl InnerResolver {
let types = types?; let types = types?;
Ok(types.map(|types| unifier.add_ty(TypeEnum::TTuple { ty: types }))) Ok(types.map(|types| unifier.add_ty(TypeEnum::TTuple { ty: types })))
} }
// special handling for option type since its class member layout in python side
// is special and cannot be mapped directly to a nac3 type as below
(TypeEnum::TObj { obj_id, params, .. }, false)
if *obj_id == primitives.option.get_obj_id(unifier) =>
{
let field_data = match obj.getattr("_nac3_option") {
Ok(d) => d,
// we use `none = Option(None)`, so the obj always have attr `_nac3_option`
Err(_) => unreachable!("cannot be None")
};
// if is `none`
let zelf_id: u64 = self.helper.id_fn.call1(py, (obj,))?.extract(py)?;
if zelf_id == self.primitive_ids.none {
if let TypeEnum::TObj { params, .. } =
unifier.get_ty_immutable(primitives.option).as_ref()
{
let var_map = params
.iter()
.map(|(id_var, ty)| {
if let TypeEnum::TVar { id, range, name, loc, .. } = &*unifier.get_ty(*ty) {
assert_eq!(*id, *id_var);
(*id, unifier.get_fresh_var_with_range(range, *name, *loc).0)
} else {
unreachable!()
}
})
.collect::<HashMap<_, _>>();
return Ok(Ok(unifier.subst(primitives.option, &var_map).unwrap()))
} else {
unreachable!("must be tobj")
}
}
let ty = match self.get_obj_type(py, field_data, unifier, defs, primitives)? {
Ok(t) => t,
Err(e) => {
return Ok(Err(format!(
"error when getting type of the option object ({})",
e
)))
}
};
let new_var_map: HashMap<_, _> = params.iter().map(|(id, _)| (*id, ty)).collect();
let res = unifier.subst(extracted_ty, &new_var_map).unwrap_or(extracted_ty);
Ok(Ok(res))
}
(TypeEnum::TObj { params, fields, .. }, false) => { (TypeEnum::TObj { params, fields, .. }, false) => {
self.pyid_to_type.write().insert(py_obj_id, extracted_ty);
let var_map = params let var_map = params
.iter() .iter()
.map(|(id_var, ty)| { .map(|(id_var, ty)| {
@ -623,13 +674,19 @@ impl InnerResolver {
let extracted_ty = unifier.subst(extracted_ty, &var_map).unwrap_or(extracted_ty); let extracted_ty = unifier.subst(extracted_ty, &var_map).unwrap_or(extracted_ty);
Ok(Ok(extracted_ty)) Ok(Ok(extracted_ty))
}; };
instantiate_obj() let result = instantiate_obj();
// update/remove the cache according to the result
match result {
Ok(Ok(ty)) => self.pyid_to_type.write().insert(py_obj_id, ty),
_ => self.pyid_to_type.write().remove(&py_obj_id)
};
result
} }
_ => Ok(Ok(extracted_ty)), _ => Ok(Ok(extracted_ty)),
} }
} }
fn get_obj_value<'ctx, 'a>( pub fn get_obj_value<'ctx, 'a>(
&self, &self,
py: Python, py: Python,
obj: &PyAny, obj: &PyAny,
@ -697,13 +754,13 @@ impl InnerResolver {
.struct_type(&[ty.ptr_type(AddressSpace::Generic).into(), size_t.into()], false); .struct_type(&[ty.ptr_type(AddressSpace::Generic).into(), size_t.into()], false);
{ {
if self.global_value_ids.read().contains(&id) { if self.global_value_ids.read().contains_key(&id) {
let global = ctx.module.get_global(&id_str).unwrap_or_else(|| { let global = ctx.module.get_global(&id_str).unwrap_or_else(|| {
ctx.module.add_global(arr_ty, Some(AddressSpace::Generic), &id_str) ctx.module.add_global(arr_ty, Some(AddressSpace::Generic), &id_str)
}); });
return Ok(Some(global.as_pointer_value().into())); return Ok(Some(global.as_pointer_value().into()));
} else { } else {
self.global_value_ids.write().insert(id); self.global_value_ids.write().insert(id, obj.into());
} }
} }
@ -751,50 +808,46 @@ impl InnerResolver {
Ok(Some(global.as_pointer_value().into())) Ok(Some(global.as_pointer_value().into()))
} else if ty_id == self.primitive_ids.tuple { } else if ty_id == self.primitive_ids.tuple {
let id_str = id.to_string();
if let Some(global) = ctx.module.get_global(&id_str) {
return Ok(Some(global.as_pointer_value().into()));
}
let elements: &PyTuple = obj.cast_as()?; let elements: &PyTuple = obj.cast_as()?;
let types: Result<Result<Vec<_>, _>, _> = elements
.iter()
.enumerate()
.map(|(i, elem)| {
self.get_obj_type(
py,
elem,
&mut ctx.unifier,
&ctx.top_level.definitions.read(),
&ctx.primitives,
)
.map_err(|e| super::CompileError::new_err(format!("Error getting element {}: {}", i, e)))
.map(|ty| ty.map(|ty| ctx.get_llvm_type(generator, ty)))
})
.collect();
let types = types?.unwrap();
let ty = ctx.ctx.struct_type(&types, false);
{
if self.global_value_ids.read().contains(&id) {
let global = ctx.module.get_global(&id_str).unwrap_or_else(|| {
ctx.module.add_global(ty, Some(AddressSpace::Generic), &id_str)
});
return Ok(Some(global.as_pointer_value().into()));
} else {
self.global_value_ids.write().insert(id);
}
}
let val: Result<Option<Vec<_>>, _> = let val: Result<Option<Vec<_>>, _> =
elements.iter().enumerate().map(|(i, elem)| self.get_obj_value(py, elem, ctx, generator).map_err(|e| elements.iter().enumerate().map(|(i, elem)| self.get_obj_value(py, elem, ctx, generator).map_err(|e|
super::CompileError::new_err(format!("Error getting element {}: {}", i, e)))).collect(); super::CompileError::new_err(format!("Error getting element {}: {}", i, e)))).collect();
let val = val?.unwrap(); let val = val?.unwrap();
let val = ctx.ctx.const_struct(&val, false); let val = ctx.ctx.const_struct(&val, false);
let global = ctx.module.add_global(ty, Some(AddressSpace::Generic), &id_str); Ok(Some(val.into()))
global.set_initializer(&val); } else if ty_id == self.primitive_ids.option {
Ok(Some(global.as_pointer_value().into())) if id == self.primitive_ids.none {
// for option type, just a null ptr, whose type needs to be casted in codegen
// according to the type info attached in the ast
Ok(Some(ctx.ctx.i8_type().ptr_type(AddressSpace::Generic).const_null().into()))
} else {
match self
.get_obj_value(py, obj.getattr("_nac3_option").unwrap(), ctx, generator)
.map_err(|e| {
super::CompileError::new_err(format!(
"Error getting value of Option object: {}",
e
))
})? {
Some(v) => {
let global_str = format!("{}_option", id);
{
if self.global_value_ids.read().contains_key(&id) {
let global = ctx.module.get_global(&global_str).unwrap_or_else(|| {
ctx.module.add_global(v.get_type(), Some(AddressSpace::Generic), &global_str)
});
return Ok(Some(global.as_pointer_value().into()));
} else {
self.global_value_ids.write().insert(id, obj.into());
}
}
let global = ctx.module.add_global(v.get_type(), Some(AddressSpace::Generic), &global_str);
global.set_initializer(&v);
Ok(Some(global.as_pointer_value().into()))
},
None => Ok(None),
}
}
} else { } else {
let id_str = id.to_string(); let id_str = id.to_string();
@ -812,13 +865,13 @@ impl InnerResolver {
.get_element_type() .get_element_type()
.into_struct_type(); .into_struct_type();
{ {
if self.global_value_ids.read().contains(&id) { if self.global_value_ids.read().contains_key(&id) {
let global = ctx.module.get_global(&id_str).unwrap_or_else(|| { let global = ctx.module.get_global(&id_str).unwrap_or_else(|| {
ctx.module.add_global(ty, Some(AddressSpace::Generic), &id_str) ctx.module.add_global(ty, Some(AddressSpace::Generic), &id_str)
}); });
return Ok(Some(global.as_pointer_value().into())); return Ok(Some(global.as_pointer_value().into()));
} else { } else {
self.global_value_ids.write().insert(id); self.global_value_ids.write().insert(id, obj.into());
} }
} }
// should be classes // should be classes
@ -827,15 +880,32 @@ impl InnerResolver {
if let TopLevelDef::Class { fields, .. } = &*definition { if let TopLevelDef::Class { fields, .. } = &*definition {
let values: Result<Option<Vec<_>>, _> = fields let values: Result<Option<Vec<_>>, _> = fields
.iter() .iter()
.map(|(name, _, _)| { .map(|(name, ty, _)| {
self.get_obj_value(py, obj.getattr(&name.to_string())?, ctx, generator).map_err(|e| let v = self.get_obj_value(py, obj.getattr(&name.to_string())?, ctx, generator)
super::CompileError::new_err(format!("Error getting field {}: {}", name, e))) .map_err(|e| super::CompileError::new_err(format!("Error getting field {}: {}", name, e)));
match (v, ctx.unifier.get_ty_immutable(*ty).as_ref()) {
(Ok(Some(v)), TypeEnum::TObj { obj_id, params, .. })
if *obj_id == ctx.primitives.option.get_obj_id(&ctx.unifier) =>
{
let actual_ptr_ty = ctx
.get_llvm_type(generator, *params.iter().next().unwrap().1)
.ptr_type(AddressSpace::Generic);
Ok(Some(ctx.builder.build_bitcast(
v,
actual_ptr_ty,
"option_none_ptr_cast",
)))
}
(v, _) => v,
}
}) })
.collect(); .collect();
let values = values?; let values = values?;
if let Some(values) = values { if let Some(values) = values {
let val = ty.const_named_struct(&values); let val = ty.const_named_struct(&values);
let global = ctx.module.add_global(ty, Some(AddressSpace::Generic), &id_str); let global = ctx.module.get_global(&id_str).unwrap_or_else(|| {
ctx.module.add_global(ty, Some(AddressSpace::Generic), &id_str)
});
global.set_initializer(&val); global.set_initializer(&val);
Ok(Some(global.as_pointer_value().into())) Ok(Some(global.as_pointer_value().into()))
} else { } else {
@ -852,6 +922,7 @@ impl InnerResolver {
py: Python, py: Python,
obj: &PyAny, obj: &PyAny,
) -> PyResult<Result<SymbolValue, String>> { ) -> PyResult<Result<SymbolValue, String>> {
let id: u64 = self.helper.id_fn.call1(py, (obj,))?.extract(py)?;
let ty_id: u64 = let ty_id: u64 =
self.helper.id_fn.call1(py, (self.helper.type_fn.call1(py, (obj,))?,))?.extract(py)?; self.helper.id_fn.call1(py, (self.helper.type_fn.call1(py, (obj,))?,))?.extract(py)?;
Ok(if ty_id == self.primitive_ids.int || ty_id == self.primitive_ids.int32 { Ok(if ty_id == self.primitive_ids.int || ty_id == self.primitive_ids.int32 {
@ -860,6 +931,12 @@ impl InnerResolver {
} else if ty_id == self.primitive_ids.int64 { } else if ty_id == self.primitive_ids.int64 {
let val: i64 = obj.extract()?; let val: i64 = obj.extract()?;
Ok(SymbolValue::I64(val)) Ok(SymbolValue::I64(val))
} else if ty_id == self.primitive_ids.uint32 {
let val: u32 = obj.extract()?;
Ok(SymbolValue::U32(val))
} else if ty_id == self.primitive_ids.uint64 {
let val: u64 = obj.extract()?;
Ok(SymbolValue::U64(val))
} else if ty_id == self.primitive_ids.bool { } else if ty_id == self.primitive_ids.bool {
let val: bool = obj.extract()?; let val: bool = obj.extract()?;
Ok(SymbolValue::Bool(val)) Ok(SymbolValue::Bool(val))
@ -870,13 +947,17 @@ impl InnerResolver {
let elements: &PyTuple = obj.cast_as()?; let elements: &PyTuple = obj.cast_as()?;
let elements: Result<Result<Vec<_>, String>, _> = let elements: Result<Result<Vec<_>, String>, _> =
elements.iter().map(|elem| self.get_default_param_obj_value(py, elem)).collect(); elements.iter().map(|elem| self.get_default_param_obj_value(py, elem)).collect();
let elements = match elements? { elements?.map(SymbolValue::Tuple)
Ok(el) => el, } else if ty_id == self.primitive_ids.option {
Err(err) => return Ok(Err(err)), if id == self.primitive_ids.none {
}; Ok(SymbolValue::OptionNone)
Ok(SymbolValue::Tuple(elements)) } else {
self
.get_default_param_obj_value(py, obj.getattr("_nac3_option").unwrap())?
.map(|v| SymbolValue::OptionSome(Box::new(v)))
}
} else { } else {
Err("only primitives values and tuple can be default parameter value".into()) Err("only primitives values, option and tuple can be default parameter value".into())
}) })
} }
} }
@ -892,8 +973,9 @@ impl SymbolResolver for Resolver {
for (key, val) in members.iter() { for (key, val) in members.iter() {
let key: &str = key.extract()?; let key: &str = key.extract()?;
if key == id.to_string() { if key == id.to_string() {
sym_value = if let Ok(Ok(v)) = self.0.get_default_param_obj_value(py, val) {
Some(self.0.get_default_param_obj_value(py, val).unwrap().unwrap()); sym_value = Some(v)
}
break; break;
} }
} }
@ -901,7 +983,7 @@ impl SymbolResolver for Resolver {
}) })
.unwrap() .unwrap()
} }
_ => unimplemented!("other type of expr not supported at {}", expr.location), _ => unreachable!("only for resolving names"),
} }
} }

View File

@ -91,6 +91,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
&mut self, &mut self,
generator: &mut dyn CodeGenerator, generator: &mut dyn CodeGenerator,
val: &SymbolValue, val: &SymbolValue,
ty: Type,
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
match val { match val {
SymbolValue::I32(v) => self.ctx.i32_type().const_int(*v as u64, true).into(), SymbolValue::I32(v) => self.ctx.i32_type().const_int(*v as u64, true).into(),
@ -107,7 +108,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
ty.const_named_struct(&[str_ptr, size.into()]).into() ty.const_named_struct(&[str_ptr, size.into()]).into()
} }
SymbolValue::Tuple(ls) => { SymbolValue::Tuple(ls) => {
let vals = ls.iter().map(|v| self.gen_symbol_val(generator, v)).collect_vec(); let vals = ls.iter().map(|v| self.gen_symbol_val(generator, v, ty)).collect_vec();
let fields = vals.iter().map(|v| v.get_type()).collect_vec(); let fields = vals.iter().map(|v| v.get_type()).collect_vec();
let ty = self.ctx.struct_type(&fields, false); let ty = self.ctx.struct_type(&fields, false);
let ptr = self.builder.build_alloca(ty, "tuple"); let ptr = self.builder.build_alloca(ty, "tuple");
@ -124,6 +125,37 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
} }
self.builder.build_load(ptr, "tup_val") self.builder.build_load(ptr, "tup_val")
} }
SymbolValue::OptionSome(v) => {
let ty = match self.unifier.get_ty_immutable(ty).as_ref() {
TypeEnum::TObj { obj_id, params, .. }
if *obj_id == self.primitives.option.get_obj_id(&self.unifier) =>
{
*params.iter().next().unwrap().1
}
_ => unreachable!("must be option type"),
};
let val = self.gen_symbol_val(generator, v, ty);
let ptr = self.builder.build_alloca(val.get_type(), "default_opt_some");
self.builder.build_store(ptr, val);
ptr.into()
}
SymbolValue::OptionNone => {
let ty = match self.unifier.get_ty_immutable(ty).as_ref() {
TypeEnum::TObj { obj_id, params, .. }
if *obj_id == self.primitives.option.get_obj_id(&self.unifier) =>
{
*params.iter().next().unwrap().1
}
_ => unreachable!("must be option type"),
};
let actual_ptr_type =
self.get_llvm_type(generator, ty).ptr_type(AddressSpace::Generic);
self.builder.build_bitcast(
self.ctx.i8_type().ptr_type(AddressSpace::Generic).const_null(),
actual_ptr_type,
"default_opt_none",
)
}
} }
} }
@ -138,6 +170,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
&mut self.unifier, &mut self.unifier,
self.top_level, self.top_level,
&mut self.type_cache, &mut self.type_cache,
&self.primitives,
ty, ty,
) )
} }
@ -205,6 +238,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
pub fn gen_int_ops( pub fn gen_int_ops(
&mut self, &mut self,
generator: &mut dyn CodeGenerator,
op: &Operator, op: &Operator,
lhs: BasicValueEnum<'ctx>, lhs: BasicValueEnum<'ctx>,
rhs: BasicValueEnum<'ctx>, rhs: BasicValueEnum<'ctx>,
@ -240,7 +274,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
(Operator::RShift, _) => self.builder.build_right_shift(lhs, rhs, true, "rshift").into(), (Operator::RShift, _) => self.builder.build_right_shift(lhs, rhs, true, "rshift").into(),
(Operator::FloorDiv, true) => self.builder.build_int_signed_div(lhs, rhs, "floordiv").into(), (Operator::FloorDiv, true) => self.builder.build_int_signed_div(lhs, rhs, "floordiv").into(),
(Operator::FloorDiv, false) => self.builder.build_int_unsigned_div(lhs, rhs, "floordiv").into(), (Operator::FloorDiv, false) => self.builder.build_int_unsigned_div(lhs, rhs, "floordiv").into(),
(Operator::Pow, s) => integer_power(self, lhs, rhs, s).into(), (Operator::Pow, s) => integer_power(generator, self, lhs, rhs, s).into(),
// special implementation? // special implementation?
(Operator::MatMult, _) => unreachable!(), (Operator::MatMult, _) => unreachable!(),
} }
@ -354,17 +388,17 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
} }
} }
pub fn gen_string<G: CodeGenerator, S: Into<String>>( pub fn gen_string<S: Into<String>>(
&mut self, &mut self,
generator: &mut G, generator: &mut dyn CodeGenerator,
s: S, s: S,
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
self.gen_const(generator, &nac3parser::ast::Constant::Str(s.into()), self.primitives.str) self.gen_const(generator, &nac3parser::ast::Constant::Str(s.into()), self.primitives.str)
} }
pub fn raise_exn<G: CodeGenerator>( pub fn raise_exn(
&mut self, &mut self,
generator: &mut G, generator: &mut dyn CodeGenerator,
name: &str, name: &str,
msg: BasicValueEnum<'ctx>, msg: BasicValueEnum<'ctx>,
params: [Option<IntValue<'ctx>>; 3], params: [Option<IntValue<'ctx>>; 3],
@ -401,14 +435,27 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
gen_raise(generator, self, Some(&zelf.into()), loc); gen_raise(generator, self, Some(&zelf.into()), loc);
} }
pub fn make_assert<G: CodeGenerator>( pub fn make_assert(
&mut self, &mut self,
generator: &mut G, generator: &mut dyn CodeGenerator,
cond: IntValue<'ctx>, cond: IntValue<'ctx>,
err_name: &str, err_name: &str,
err_msg: &str, err_msg: &str,
params: [Option<IntValue<'ctx>>; 3], params: [Option<IntValue<'ctx>>; 3],
loc: Location, loc: Location,
) {
let err_msg = self.gen_string(generator, err_msg);
self.make_assert_impl(generator, cond, err_name, err_msg, params, loc)
}
pub fn make_assert_impl(
&mut self,
generator: &mut dyn CodeGenerator,
cond: IntValue<'ctx>,
err_name: &str,
err_msg: BasicValueEnum<'ctx>,
params: [Option<IntValue<'ctx>>; 3],
loc: Location,
) { ) {
let i1 = self.ctx.bool_type(); let i1 = self.ctx.bool_type();
let i1_true = i1.const_all_ones(); let i1_true = i1.const_all_ones();
@ -435,7 +482,6 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
let exn_block = self.ctx.append_basic_block(current_fun, "fail"); let exn_block = self.ctx.append_basic_block(current_fun, "fail");
self.builder.build_conditional_branch(cond, then_block, exn_block); self.builder.build_conditional_branch(cond, then_block, exn_block);
self.builder.position_at_end(exn_block); self.builder.position_at_end(exn_block);
let err_msg = self.gen_string(generator, err_msg);
self.raise_exn(generator, err_name, err_msg, params, loc); self.raise_exn(generator, err_name, err_msg, params, loc);
self.builder.position_at_end(then_block); self.builder.position_at_end(then_block);
} }
@ -592,7 +638,7 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
} }
mapping.insert( mapping.insert(
k.name, k.name,
ctx.gen_symbol_val(generator, &k.default_value.unwrap()).into(), ctx.gen_symbol_val(generator, &k.default_value.unwrap(), k.ty).into(),
); );
} }
// reorder the parameters // reorder the parameters
@ -895,23 +941,20 @@ pub fn gen_binop_expr<'ctx, 'a, G: CodeGenerator>(
// which would be unchanged until further unification, which we would never do // which would be unchanged until further unification, which we would never do
// when doing code generation for function instances // when doing code generation for function instances
Ok(if ty1 == ty2 && [ctx.primitives.int32, ctx.primitives.int64].contains(&ty1) { Ok(if ty1 == ty2 && [ctx.primitives.int32, ctx.primitives.int64].contains(&ty1) {
ctx.gen_int_ops(op, left, right, true) ctx.gen_int_ops(generator, op, left, right, true)
} else if ty1 == ty2 && [ctx.primitives.uint32, ctx.primitives.uint64].contains(&ty1) { } else if ty1 == ty2 && [ctx.primitives.uint32, ctx.primitives.uint64].contains(&ty1) {
ctx.gen_int_ops(op, left, right, false) ctx.gen_int_ops(generator, op, left, right, false)
} else if ty1 == ty2 && ctx.primitives.float == ty1 { } else if ty1 == ty2 && ctx.primitives.float == ty1 {
ctx.gen_float_ops(op, left, right) ctx.gen_float_ops(op, left, right)
} else if ty1 == ctx.primitives.float && ty2 == ctx.primitives.int32 { } else if ty1 == ctx.primitives.float && ty2 == ctx.primitives.int32 {
// Pow is the only operator that would pass typecheck between float and int // Pow is the only operator that would pass typecheck between float and int
assert!(*op == Operator::Pow); assert!(*op == Operator::Pow);
// TODO: throw exception when rhs is out of i16 bound let i32_t = ctx.ctx.i32_type();
// since llvm intrinsic only support to i16 for f64 let pow_intr = ctx.module.get_function("llvm.powi.f64.i32").unwrap_or_else(|| {
let i16_t = ctx.ctx.i16_type();
let pow_intr = ctx.module.get_function("llvm.powi.f64.i16").unwrap_or_else(|| {
let f64_t = ctx.ctx.f64_type(); let f64_t = ctx.ctx.f64_type();
let ty = f64_t.fn_type(&[f64_t.into(), i16_t.into()], false); let ty = f64_t.fn_type(&[f64_t.into(), i32_t.into()], false);
ctx.module.add_function("llvm.powi.f64.i16", ty, None) ctx.module.add_function("llvm.powi.f64.i32", ty, None)
}); });
let right = ctx.builder.build_int_truncate(right.into_int_value(), i16_t, "r_pow");
ctx.builder ctx.builder
.build_call(pow_intr, &[left.into(), right.into()], "f_pow_i") .build_call(pow_intr, &[left.into(), right.into()], "f_pow_i")
.try_as_basic_value() .try_as_basic_value()
@ -927,6 +970,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
ctx: &mut CodeGenContext<'ctx, 'a>, ctx: &mut CodeGenContext<'ctx, 'a>,
expr: &Expr<Option<Type>>, expr: &Expr<Option<Type>>,
) -> Result<Option<ValueEnum<'ctx>>, String> { ) -> Result<Option<ValueEnum<'ctx>>, String> {
ctx.current_loc = expr.location;
let int32 = ctx.ctx.i32_type(); let int32 = ctx.ctx.i32_type();
let zero = int32.const_int(0, false); let zero = int32.const_int(0, false);
Ok(Some(match &expr.node { Ok(Some(match &expr.node {
@ -934,23 +978,45 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
let ty = expr.custom.unwrap(); let ty = expr.custom.unwrap();
ctx.gen_const(generator, value, ty).into() ctx.gen_const(generator, value, ty).into()
} }
ExprKind::Name { id, .. } if id == &"none".into() => {
match (
ctx.unifier.get_ty(expr.custom.unwrap()).as_ref(),
ctx.unifier.get_ty(ctx.primitives.option).as_ref(),
) {
(
TypeEnum::TObj { obj_id, params, .. },
TypeEnum::TObj { obj_id: opt_id, .. },
) if *obj_id == *opt_id => ctx
.get_llvm_type(generator, *params.iter().next().unwrap().1)
.ptr_type(AddressSpace::Generic)
.const_null()
.into(),
_ => unreachable!("must be option type"),
}
}
ExprKind::Name { id, .. } => match ctx.var_assignment.get(id) { ExprKind::Name { id, .. } => match ctx.var_assignment.get(id) {
Some((ptr, None, _)) => ctx.builder.build_load(*ptr, "load").into(), Some((ptr, None, _)) => ctx.builder.build_load(*ptr, "load").into(),
Some((_, Some(static_value), _)) => ValueEnum::Static(static_value.clone()), Some((_, Some(static_value), _)) => ValueEnum::Static(static_value.clone()),
None => { None => {
let resolver = ctx.resolver.clone(); let resolver = ctx.resolver.clone();
let val = resolver.get_symbol_value(*id, ctx).unwrap(); let val = resolver.get_symbol_value(*id, ctx).unwrap();
// if is tuple, need to deref it to handle tuple as value // if is option, need to cast pointer to handle None
if let (TypeEnum::TTuple { .. }, BasicValueEnum::PointerValue(ptr)) = ( match &*ctx.unifier.get_ty(expr.custom.unwrap()) {
&*ctx.unifier.get_ty(expr.custom.unwrap()), TypeEnum::TObj { obj_id, params, .. }
resolver if *obj_id == ctx.primitives.option.get_obj_id(&ctx.unifier) =>
.get_symbol_value(*id, ctx) {
.unwrap() if let BasicValueEnum::PointerValue(ptr) = val.to_basic_value_enum(ctx, generator)? {
.to_basic_value_enum(ctx, generator)?, let actual_ptr_ty = ctx.get_llvm_type(
) { generator,
ctx.builder.build_load(ptr, "tup_val").into() *params.iter().next().unwrap().1,
} else { )
val .ptr_type(AddressSpace::Generic);
ctx.builder.build_bitcast(ptr, actual_ptr_ty, "option_ptr_cast").into()
} else {
unreachable!("option obj must be ptr")
}
}
_ => val,
} }
} }
}, },
@ -962,7 +1028,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
.map(|x| { .map(|x| {
generator generator
.gen_expr(ctx, x) .gen_expr(ctx, x)
.map_or_else(|e| Err(e), |v| v.unwrap().to_basic_value_enum(ctx, generator)) .map_or_else(Err, |v| v.unwrap().to_basic_value_enum(ctx, generator))
}) })
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
let ty = if elements.is_empty() { let ty = if elements.is_empty() {
@ -995,7 +1061,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
.map(|x| { .map(|x| {
generator generator
.gen_expr(ctx, x) .gen_expr(ctx, x)
.map_or_else(|e| Err(e), |v| v.unwrap().to_basic_value_enum(ctx, generator)) .map_or_else(Err, |v| v.unwrap().to_basic_value_enum(ctx, generator))
}) })
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
let element_ty = element_val.iter().map(BasicValueEnum::get_type).collect_vec(); let element_ty = element_val.iter().map(BasicValueEnum::get_type).collect_vec();
@ -1023,7 +1089,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
v.into_pointer_value(), v.into_pointer_value(),
&[zero, int32.const_int(index as u64, false)], &[zero, int32.const_int(index as u64, false)],
))) as Result<_, String> ))) as Result<_, String>
}, |v| Ok(v))?, }, Ok)?,
ValueEnum::Dynamic(v) => { ValueEnum::Dynamic(v) => {
let index = ctx.get_attr_index(value.custom.unwrap(), *attr); let index = ctx.get_attr_index(value.custom.unwrap(), *attr);
ValueEnum::Dynamic(ctx.build_gep_and_load( ValueEnum::Dynamic(ctx.build_gep_and_load(
@ -1204,21 +1270,44 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
.unwrap() .unwrap()
.to_basic_value_enum(ctx, generator)? .to_basic_value_enum(ctx, generator)?
.into_int_value(); .into_int_value();
let body_ty = body.custom.unwrap();
let is_none = ctx.unifier.get_representative(body_ty) == ctx.primitives.none;
let result = if !is_none {
let llvm_ty = ctx.get_llvm_type(generator, body_ty);
Some(ctx.builder.build_alloca(llvm_ty, "if_exp_result"))
} else {
None
};
let current = ctx.builder.get_insert_block().unwrap().get_parent().unwrap(); let current = ctx.builder.get_insert_block().unwrap().get_parent().unwrap();
let then_bb = ctx.ctx.append_basic_block(current, "then"); let then_bb = ctx.ctx.append_basic_block(current, "then");
let else_bb = ctx.ctx.append_basic_block(current, "else"); let else_bb = ctx.ctx.append_basic_block(current, "else");
let cont_bb = ctx.ctx.append_basic_block(current, "cont"); let cont_bb = ctx.ctx.append_basic_block(current, "cont");
ctx.builder.build_conditional_branch(test, then_bb, else_bb); ctx.builder.build_conditional_branch(test, then_bb, else_bb);
ctx.builder.position_at_end(then_bb); ctx.builder.position_at_end(then_bb);
let a = generator.gen_expr(ctx, body)?.unwrap().to_basic_value_enum(ctx, generator)?; let a = generator.gen_expr(ctx, body)?;
match result {
None => None,
Some(v) => {
let a = a.unwrap().to_basic_value_enum(ctx, generator)?;
Some(ctx.builder.build_store(v, a))
}
};
ctx.builder.build_unconditional_branch(cont_bb); ctx.builder.build_unconditional_branch(cont_bb);
ctx.builder.position_at_end(else_bb); ctx.builder.position_at_end(else_bb);
let b = generator.gen_expr(ctx, orelse)?.unwrap().to_basic_value_enum(ctx, generator)?; let b = generator.gen_expr(ctx, orelse)?;
match result {
None => None,
Some(v) => {
let b = b.unwrap().to_basic_value_enum(ctx, generator)?;
Some(ctx.builder.build_store(v, b))
}
};
ctx.builder.build_unconditional_branch(cont_bb); ctx.builder.build_unconditional_branch(cont_bb);
ctx.builder.position_at_end(cont_bb); ctx.builder.position_at_end(cont_bb);
let phi = ctx.builder.build_phi(a.get_type(), "ifexpr"); match result {
phi.add_incoming(&[(&a, then_bb), (&b, else_bb)]); None => return Ok(None),
phi.as_basic_value().into() Some(v) => return Ok(Some(ctx.builder.build_load(v, "if_exp_val_load").into()))
}
} }
ExprKind::Call { func, args, keywords } => { ExprKind::Call { func, args, keywords } => {
let mut params = args let mut params = args
@ -1307,6 +1396,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
let (start, end, step) = let (start, end, step) =
handle_slice_indices(lower, upper, step, ctx, generator, v)?; handle_slice_indices(lower, upper, step, ctx, generator, v)?;
let length = calculate_len_for_slice_range( let length = calculate_len_for_slice_range(
generator,
ctx, ctx,
start, start,
ctx.builder ctx.builder
@ -1328,8 +1418,8 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
let res_ind = let res_ind =
handle_slice_indices(&None, &None, &None, ctx, generator, res_array_ret)?; handle_slice_indices(&None, &None, &None, ctx, generator, res_array_ret)?;
list_slice_assignment( list_slice_assignment(
generator,
ctx, ctx,
generator.get_size_type(ctx.ctx),
ty, ty,
res_array_ret, res_array_ret,
res_ind, res_ind,

View File

@ -6,7 +6,7 @@ use inkwell::{
context::Context, context::Context,
memory_buffer::MemoryBuffer, memory_buffer::MemoryBuffer,
module::Module, module::Module,
types::{BasicTypeEnum, IntType}, types::BasicTypeEnum,
values::{IntValue, PointerValue}, values::{IntValue, PointerValue},
AddressSpace, IntPredicate, AddressSpace, IntPredicate,
}; };
@ -34,6 +34,7 @@ pub fn load_irrt(ctx: &Context) -> Module {
// repeated squaring method adapted from GNU Scientific Library: // repeated squaring method adapted from GNU Scientific Library:
// https://git.savannah.gnu.org/cgit/gsl.git/tree/sys/pow_int.c // https://git.savannah.gnu.org/cgit/gsl.git/tree/sys/pow_int.c
pub fn integer_power<'ctx, 'a>( pub fn integer_power<'ctx, 'a>(
generator: &mut dyn CodeGenerator,
ctx: &mut CodeGenContext<'ctx, 'a>, ctx: &mut CodeGenContext<'ctx, 'a>,
base: IntValue<'ctx>, base: IntValue<'ctx>,
exp: IntValue<'ctx>, exp: IntValue<'ctx>,
@ -51,7 +52,21 @@ pub fn integer_power<'ctx, 'a>(
let fn_type = base_type.fn_type(&[base_type.into(), base_type.into()], false); let fn_type = base_type.fn_type(&[base_type.into(), base_type.into()], false);
ctx.module.add_function(symbol, fn_type, None) ctx.module.add_function(symbol, fn_type, None)
}); });
// TODO: throw exception when exp < 0 // throw exception when exp < 0
let ge_zero = ctx.builder.build_int_compare(
IntPredicate::SGE,
exp,
exp.get_type().const_zero(),
"assert_int_pow_ge_0",
);
ctx.make_assert(
generator,
ge_zero,
"0:ValueError",
"integer power must be positive or zero",
[None, None, None],
ctx.current_loc,
);
ctx.builder ctx.builder
.build_call(pow_fun, &[base.into(), exp.into()], "call_int_pow") .build_call(pow_fun, &[base.into(), exp.into()], "call_int_pow")
.try_as_basic_value() .try_as_basic_value()
@ -60,6 +75,7 @@ pub fn integer_power<'ctx, 'a>(
} }
pub fn calculate_len_for_slice_range<'ctx, 'a>( pub fn calculate_len_for_slice_range<'ctx, 'a>(
generator: &mut dyn CodeGenerator,
ctx: &mut CodeGenContext<'ctx, 'a>, ctx: &mut CodeGenContext<'ctx, 'a>,
start: IntValue<'ctx>, start: IntValue<'ctx>,
end: IntValue<'ctx>, end: IntValue<'ctx>,
@ -72,7 +88,21 @@ pub fn calculate_len_for_slice_range<'ctx, 'a>(
ctx.module.add_function(SYMBOL, fn_t, None) ctx.module.add_function(SYMBOL, fn_t, None)
}); });
// TODO: assert step != 0, throw exception if not // assert step != 0, throw exception if not
let not_zero = ctx.builder.build_int_compare(
IntPredicate::NE,
step,
step.get_type().const_zero(),
"range_step_ne",
);
ctx.make_assert(
generator,
not_zero,
"0:ValueError",
"step must not be zero",
[None, None, None],
ctx.current_loc,
);
ctx.builder ctx.builder
.build_call(len_func, &[start.into(), end.into(), step.into()], "calc_len") .build_call(len_func, &[start.into(), end.into(), step.into()], "calc_len")
.try_as_basic_value() .try_as_basic_value()
@ -129,7 +159,6 @@ pub fn handle_slice_indices<'a, 'ctx, G: CodeGenerator>(
generator: &mut G, generator: &mut G,
list: PointerValue<'ctx>, list: PointerValue<'ctx>,
) -> Result<(IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>), String> { ) -> Result<(IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>), String> {
// TODO: throw exception when step is 0
let int32 = ctx.ctx.i32_type(); let int32 = ctx.ctx.i32_type();
let zero = int32.const_zero(); let zero = int32.const_zero();
let one = int32.const_int(1, false); let one = int32.const_int(1, false);
@ -156,6 +185,21 @@ pub fn handle_slice_indices<'a, 'ctx, G: CodeGenerator>(
.unwrap() .unwrap()
.to_basic_value_enum(ctx, generator)? .to_basic_value_enum(ctx, generator)?
.into_int_value(); .into_int_value();
// assert step != 0, throw exception if not
let not_zero = ctx.builder.build_int_compare(
IntPredicate::NE,
step,
step.get_type().const_zero(),
"range_step_ne",
);
ctx.make_assert(
generator,
not_zero,
"0:ValueError",
"slice step cannot be zero",
[None, None, None],
ctx.current_loc,
);
let len_id = ctx.builder.build_int_sub(length, one, "lenmin1"); let len_id = ctx.builder.build_int_sub(length, one, "lenmin1");
let neg = ctx.builder.build_int_compare(IntPredicate::SLT, step, zero, "step_is_neg"); let neg = ctx.builder.build_int_compare(IntPredicate::SLT, step, zero, "step_is_neg");
( (
@ -231,14 +275,15 @@ pub fn handle_slice_index_bound<'a, 'ctx, G: CodeGenerator>(
/// Order of tuples assign_idx and value_idx is ('start', 'end', 'step'). /// Order of tuples assign_idx and value_idx is ('start', 'end', 'step').
/// Negative index should be handled before entering this function /// Negative index should be handled before entering this function
pub fn list_slice_assignment<'ctx, 'a>( pub fn list_slice_assignment<'ctx, 'a>(
generator: &mut dyn CodeGenerator,
ctx: &mut CodeGenContext<'ctx, 'a>, ctx: &mut CodeGenContext<'ctx, 'a>,
size_ty: IntType<'ctx>,
ty: BasicTypeEnum<'ctx>, ty: BasicTypeEnum<'ctx>,
dest_arr: PointerValue<'ctx>, dest_arr: PointerValue<'ctx>,
dest_idx: (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>), dest_idx: (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>),
src_arr: PointerValue<'ctx>, src_arr: PointerValue<'ctx>,
src_idx: (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>), src_idx: (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>),
) { ) {
let size_ty = generator.get_size_type(ctx.ctx);
let int8_ptr = ctx.ctx.i8_type().ptr_type(AddressSpace::Generic); let int8_ptr = ctx.ctx.i8_type().ptr_type(AddressSpace::Generic);
let int32 = ctx.ctx.i32_type(); let int32 = ctx.ctx.i32_type();
let (fun_symbol, elem_ptr_type) = ("__nac3_list_slice_assign_var_size", int8_ptr); let (fun_symbol, elem_ptr_type) = ("__nac3_list_slice_assign_var_size", int8_ptr);
@ -282,8 +327,67 @@ pub fn list_slice_assignment<'ctx, 'a>(
let src_len = ctx.builder.build_int_truncate_or_bit_cast(src_len, int32, "srclen32"); let src_len = ctx.builder.build_int_truncate_or_bit_cast(src_len, int32, "srclen32");
// index in bound and positive should be done // index in bound and positive should be done
// TODO: assert if dest.step == 1 then len(src) <= len(dest) else len(src) == len(dest), and // assert if dest.step == 1 then len(src) <= len(dest) else len(src) == len(dest), and
// throw exception if not satisfied // throw exception if not satisfied
let src_end = ctx.builder
.build_select(
ctx.builder.build_int_compare(
inkwell::IntPredicate::SLT,
src_idx.2,
zero,
"is_neg",
),
ctx.builder.build_int_sub(src_idx.1, one, "e_min_one"),
ctx.builder.build_int_add(src_idx.1, one, "e_add_one"),
"final_e",
)
.into_int_value();
let dest_end = ctx.builder
.build_select(
ctx.builder.build_int_compare(
inkwell::IntPredicate::SLT,
dest_idx.2,
zero,
"is_neg",
),
ctx.builder.build_int_sub(dest_idx.1, one, "e_min_one"),
ctx.builder.build_int_add(dest_idx.1, one, "e_add_one"),
"final_e",
)
.into_int_value();
let src_slice_len =
calculate_len_for_slice_range(generator, ctx, src_idx.0, src_end, src_idx.2);
let dest_slice_len =
calculate_len_for_slice_range(generator, ctx, dest_idx.0, dest_end, dest_idx.2);
let src_eq_dest = ctx.builder.build_int_compare(
IntPredicate::EQ,
src_slice_len,
dest_slice_len,
"slice_src_eq_dest",
);
let src_slt_dest = ctx.builder.build_int_compare(
IntPredicate::SLT,
src_slice_len,
dest_slice_len,
"slice_src_slt_dest",
);
let dest_step_eq_one = ctx.builder.build_int_compare(
IntPredicate::EQ,
dest_idx.2,
dest_idx.2.get_type().const_int(1, false),
"slice_dest_step_eq_one",
);
let cond_1 = ctx.builder.build_and(dest_step_eq_one, src_slt_dest, "slice_cond_1");
let cond = ctx.builder.build_or(src_eq_dest, cond_1, "slice_cond");
ctx.make_assert(
generator,
cond,
"0:ValueError",
"attempt to assign sequence of size {0} to slice of size {1} with step size {2}",
[Some(src_slice_len), Some(dest_slice_len), Some(dest_idx.2)],
ctx.current_loc,
);
let new_len = { let new_len = {
let args = vec![ let args = vec![
dest_idx.0.into(), // dest start idx dest_idx.0.into(), // dest start idx

View File

@ -20,7 +20,7 @@ use inkwell::{
values::{BasicValueEnum, FunctionValue, PhiValue, PointerValue} values::{BasicValueEnum, FunctionValue, PhiValue, PointerValue}
}; };
use itertools::Itertools; use itertools::Itertools;
use nac3parser::ast::{Stmt, StrRef}; use nac3parser::ast::{Stmt, StrRef, Location};
use parking_lot::{Condvar, Mutex}; use parking_lot::{Condvar, Mutex};
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::sync::{ use std::sync::{
@ -77,6 +77,7 @@ pub struct CodeGenContext<'ctx, 'a> {
pub outer_catch_clauses: pub outer_catch_clauses:
Option<(Vec<Option<BasicValueEnum<'ctx>>>, BasicBlock<'ctx>, PhiValue<'ctx>)>, Option<(Vec<Option<BasicValueEnum<'ctx>>>, BasicBlock<'ctx>, PhiValue<'ctx>)>,
pub need_sret: bool, pub need_sret: bool,
pub current_loc: Location,
} }
impl<'ctx, 'a> CodeGenContext<'ctx, 'a> { impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
@ -259,6 +260,7 @@ fn get_llvm_type<'ctx>(
unifier: &mut Unifier, unifier: &mut Unifier,
top_level: &TopLevelContext, top_level: &TopLevelContext,
type_cache: &mut HashMap<Type, BasicTypeEnum<'ctx>>, type_cache: &mut HashMap<Type, BasicTypeEnum<'ctx>>,
primitives: &PrimitiveStore,
ty: Type, ty: Type,
) -> BasicTypeEnum<'ctx> { ) -> BasicTypeEnum<'ctx> {
use TypeEnum::*; use TypeEnum::*;
@ -268,9 +270,28 @@ fn get_llvm_type<'ctx>(
let ty_enum = unifier.get_ty(ty); let ty_enum = unifier.get_ty(ty);
let result = match &*ty_enum { let result = match &*ty_enum {
TObj { obj_id, fields, .. } => { TObj { obj_id, fields, .. } => {
// check to avoid treating primitives as classes // check to avoid treating primitives other than Option as classes
if obj_id.0 <= 7 { if obj_id.0 <= 10 {
unreachable!(); match (unifier.get_ty(ty).as_ref(), unifier.get_ty(primitives.option).as_ref())
{
(
TypeEnum::TObj { obj_id, params, .. },
TypeEnum::TObj { obj_id: opt_id, .. },
) if *obj_id == *opt_id => {
return get_llvm_type(
ctx,
generator,
unifier,
top_level,
type_cache,
primitives,
*params.iter().next().unwrap().1,
)
.ptr_type(AddressSpace::Generic)
.into();
}
_ => unreachable!("must be option type"),
}
} }
// a struct with fields in the order of declaration // a struct with fields in the order of declaration
let top_level_defs = top_level.definitions.read(); let top_level_defs = top_level.definitions.read();
@ -289,6 +310,7 @@ fn get_llvm_type<'ctx>(
unifier, unifier,
top_level, top_level,
type_cache, type_cache,
primitives,
fields[&f.0].0, fields[&f.0].0,
) )
}) })
@ -304,14 +326,14 @@ fn get_llvm_type<'ctx>(
// a struct with fields in the order present in the tuple // a struct with fields in the order present in the tuple
let fields = ty let fields = ty
.iter() .iter()
.map(|ty| get_llvm_type(ctx, generator, unifier, top_level, type_cache, *ty)) .map(|ty| get_llvm_type(ctx, generator, unifier, top_level, type_cache, primitives, *ty))
.collect_vec(); .collect_vec();
ctx.struct_type(&fields, false).into() ctx.struct_type(&fields, false).into()
} }
TList { ty } => { TList { ty } => {
// a struct with an integer and a pointer to an array // a struct with an integer and a pointer to an array
let element_type = let element_type =
get_llvm_type(ctx, generator, unifier, top_level, type_cache, *ty); get_llvm_type(ctx, generator, unifier, top_level, type_cache, primitives, *ty);
let fields = [ let fields = [
element_type.ptr_type(AddressSpace::Generic).into(), element_type.ptr_type(AddressSpace::Generic).into(),
generator.get_size_type(ctx).into(), generator.get_size_type(ctx).into(),
@ -339,13 +361,14 @@ fn need_sret<'ctx>(ctx: &'ctx Context, ty: BasicTypeEnum<'ctx>) -> bool {
need_sret_impl(ctx, ty, true) need_sret_impl(ctx, ty, true)
} }
pub fn gen_func<'ctx, G: CodeGenerator>( pub fn gen_func_impl<'ctx, G: CodeGenerator, F: FnOnce(&mut G, &mut CodeGenContext) -> Result<(), String>> (
context: &'ctx Context, context: &'ctx Context,
generator: &mut G, generator: &mut G,
registry: &WorkerRegistry, registry: &WorkerRegistry,
builder: Builder<'ctx>, builder: Builder<'ctx>,
module: Module<'ctx>, module: Module<'ctx>,
task: CodeGenTask, task: CodeGenTask,
codegen_function: F
) -> Result<(Builder<'ctx>, Module<'ctx>, FunctionValue<'ctx>), (Builder<'ctx>, String)> { ) -> Result<(Builder<'ctx>, Module<'ctx>, FunctionValue<'ctx>), (Builder<'ctx>, String)> {
let top_level_ctx = registry.top_level_ctx.clone(); let top_level_ctx = registry.top_level_ctx.clone();
let static_value_store = registry.static_value_store.clone(); let static_value_store = registry.static_value_store.clone();
@ -385,6 +408,7 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
range: unifier.get_representative(primitives.range), range: unifier.get_representative(primitives.range),
str: unifier.get_representative(primitives.str), str: unifier.get_representative(primitives.str),
exception: unifier.get_representative(primitives.exception), exception: unifier.get_representative(primitives.exception),
option: unifier.get_representative(primitives.option),
}; };
let mut type_cache: HashMap<_, _> = [ let mut type_cache: HashMap<_, _> = [
@ -417,6 +441,8 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
exception.set_body(&fields, false); exception.set_body(&fields, false);
exception.ptr_type(AddressSpace::Generic).into() exception.ptr_type(AddressSpace::Generic).into()
}); });
// NOTE: special handling of option cannot use this type cache since it contains type var,
// handled inside get_llvm_type instead
let (args, ret) = if let ConcreteTypeEnum::TFunc { args, ret, .. } = let (args, ret) = if let ConcreteTypeEnum::TFunc { args, ret, .. } =
task.store.get(task.signature) task.store.get(task.signature)
@ -437,7 +463,7 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
let ret_type = if unifier.unioned(ret, primitives.none) { let ret_type = if unifier.unioned(ret, primitives.none) {
None None
} else { } else {
Some(get_llvm_type(context, generator, &mut unifier, top_level_ctx.as_ref(), &mut type_cache, ret)) Some(get_llvm_type(context, generator, &mut unifier, top_level_ctx.as_ref(), &mut type_cache, &primitives, ret))
}; };
let has_sret = ret_type.map_or(false, |ty| need_sret(context, ty)); let has_sret = ret_type.map_or(false, |ty| need_sret(context, ty));
@ -450,6 +476,7 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
&mut unifier, &mut unifier,
top_level_ctx.as_ref(), top_level_ctx.as_ref(),
&mut type_cache, &mut type_cache,
&primitives,
arg.ty, arg.ty,
) )
.into() .into()
@ -497,6 +524,7 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
&mut unifier, &mut unifier,
top_level_ctx.as_ref(), top_level_ctx.as_ref(),
&mut type_cache, &mut type_cache,
&primitives,
arg.ty, arg.ty,
), ),
&arg.name.to_string(), &arg.name.to_string(),
@ -543,28 +571,38 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
module, module,
unifier, unifier,
static_value_store, static_value_store,
need_sret: has_sret need_sret: has_sret,
current_loc: Default::default(),
}; };
let mut err = None; let result = codegen_function(generator, &mut code_gen_context);
for stmt in task.body.iter() {
if let Err(e) = generator.gen_stmt(&mut code_gen_context, stmt) {
err = Some(e);
break;
}
if code_gen_context.is_terminated() {
break;
}
}
// after static analysis, only void functions can have no return at the end. // after static analysis, only void functions can have no return at the end.
if !code_gen_context.is_terminated() { if !code_gen_context.is_terminated() {
code_gen_context.builder.build_return(None); code_gen_context.builder.build_return(None);
} }
let CodeGenContext { builder, module, .. } = code_gen_context; let CodeGenContext { builder, module, .. } = code_gen_context;
if let Some(e) = err { if let Err(e) = result {
return Err((builder, e)); return Err((builder, e));
} }
Ok((builder, module, fn_val)) Ok((builder, module, fn_val))
} }
pub fn gen_func<'ctx, G: CodeGenerator>(
context: &'ctx Context,
generator: &mut G,
registry: &WorkerRegistry,
builder: Builder<'ctx>,
module: Module<'ctx>,
task: CodeGenTask,
) -> Result<(Builder<'ctx>, Module<'ctx>, FunctionValue<'ctx>), (Builder<'ctx>, String)> {
let body = task.body.clone();
gen_func_impl(context, generator, registry, builder, module, task, |generator, ctx| {
for stmt in body.iter() {
generator.gen_stmt(ctx, stmt)?;
}
Ok(())
})
}

View File

@ -134,8 +134,8 @@ pub fn gen_assign<'ctx, 'a, G: CodeGenerator>(
}; };
let src_ind = handle_slice_indices(&None, &None, &None, ctx, generator, value)?; let src_ind = handle_slice_indices(&None, &None, &None, ctx, generator, value)?;
list_slice_assignment( list_slice_assignment(
generator,
ctx, ctx,
generator.get_size_type(ctx.ctx),
ty, ty,
ls, ls,
(start, end, step), (start, end, step),
@ -189,7 +189,8 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
if ctx.unifier.unioned(iter.custom.unwrap(), ctx.primitives.range) { if ctx.unifier.unioned(iter.custom.unwrap(), ctx.primitives.range) {
// setup // setup
let iter_val = iter_val.into_pointer_value(); let iter_val = iter_val.into_pointer_value();
let i = generator.gen_store_target(ctx, target)?; let i = generator.gen_var_alloc(ctx, int32.into())?;
let user_i = generator.gen_store_target(ctx, target)?;
let (start, end, step) = destructure_range(ctx, iter_val); let (start, end, step) = destructure_range(ctx, iter_val);
ctx.builder.build_store(i, ctx.builder.build_int_sub(start, step, "start_init")); ctx.builder.build_store(i, ctx.builder.build_int_sub(start, step, "start_init"));
ctx.builder.build_unconditional_branch(test_bb); ctx.builder.build_unconditional_branch(test_bb);
@ -207,6 +208,7 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
"start_loop", "start_loop",
); );
ctx.builder.build_store(i, tmp); ctx.builder.build_store(i, tmp);
ctx.builder.build_store(user_i, tmp);
// // if step > 0, continue when i < end // // if step > 0, continue when i < end
let cmp1 = ctx.builder.build_int_compare(inkwell::IntPredicate::SLT, tmp, end, "cmp1"); let cmp1 = ctx.builder.build_int_compare(inkwell::IntPredicate::SLT, tmp, end, "cmp1");
// if step < 0, continue when i > end // if step < 0, continue when i > end
@ -420,8 +422,8 @@ pub fn final_proxy<'ctx, 'a>(
final_paths.push(block); final_paths.push(block);
} }
pub fn get_builtins<'ctx, 'a, G: CodeGenerator>( pub fn get_builtins<'ctx, 'a>(
generator: &mut G, generator: &mut dyn CodeGenerator,
ctx: &mut CodeGenContext<'ctx, 'a>, ctx: &mut CodeGenContext<'ctx, 'a>,
symbol: &str, symbol: &str,
) -> FunctionValue<'ctx> { ) -> FunctionValue<'ctx> {
@ -517,8 +519,8 @@ pub fn exn_constructor<'ctx, 'a>(
Ok(Some(zelf.into())) Ok(Some(zelf.into()))
} }
pub fn gen_raise<'ctx, 'a, G: CodeGenerator>( pub fn gen_raise<'ctx, 'a>(
generator: &mut G, generator: &mut dyn CodeGenerator,
ctx: &mut CodeGenContext<'ctx, 'a>, ctx: &mut CodeGenContext<'ctx, 'a>,
exception: Option<&BasicValueEnum<'ctx>>, exception: Option<&BasicValueEnum<'ctx>>,
loc: Location, loc: Location,
@ -929,6 +931,7 @@ pub fn gen_stmt<'ctx, 'a, G: CodeGenerator>(
ctx: &mut CodeGenContext<'ctx, 'a>, ctx: &mut CodeGenContext<'ctx, 'a>,
stmt: &Stmt<Option<Type>>, stmt: &Stmt<Option<Type>>,
) -> Result<(), String> { ) -> Result<(), String> {
ctx.current_loc = stmt.location;
match &stmt.node { match &stmt.node {
StmtKind::Pass { .. } => {} StmtKind::Pass { .. } => {}
StmtKind::Expr { value, .. } => { StmtKind::Expr { value, .. } => {
@ -973,7 +976,25 @@ pub fn gen_stmt<'ctx, 'a, G: CodeGenerator>(
gen_raise(generator, ctx, None, stmt.location); gen_raise(generator, ctx, None, stmt.location);
} }
} }
_ => unimplemented!(), StmtKind::Assert { test, msg, .. } => {
let test =
generator.gen_expr(ctx, test)?.unwrap().to_basic_value_enum(ctx, generator)?;
let err_msg = match msg {
Some(msg) => {
generator.gen_expr(ctx, msg)?.unwrap().to_basic_value_enum(ctx, generator)?
}
None => ctx.gen_string(generator, ""),
};
ctx.make_assert_impl(
generator,
test.into_int_value(),
"0:AssertionError",
err_msg,
[None, None, None],
stmt.location,
)
}
_ => unimplemented!()
}; };
Ok(()) Ok(())
} }

View File

@ -71,7 +71,7 @@ impl SymbolResolver for Resolver {
unimplemented!() unimplemented!()
} }
fn get_exception_id(&self, tyid: usize) -> usize { fn get_exception_id(&self, _tyid: usize) -> usize {
unimplemented!() unimplemented!()
} }
} }
@ -180,13 +180,22 @@ fn test_primitives() {
let expected = indoc! {" let expected = indoc! {"
; ModuleID = 'test' ; ModuleID = 'test'
source_filename = \"test\" source_filename = \"test\"
define i32 @testing(i32 %0, i32 %1) { define i32 @testing(i32 %0, i32 %1) {
init: init:
%add = add i32 %0, %1 %add = add i32 %0, %1
%cmp = icmp eq i32 %add, 1 %cmp = icmp eq i32 %add, 1
%ifexpr = select i1 %cmp, i32 %0, i32 0 br i1 %cmp, label %then, label %else
ret i32 %ifexpr
then: ; preds = %init
br label %cont
else: ; preds = %init
br label %cont
cont: ; preds = %else, %then
%if_exp_result.0 = phi i32 [ %0, %then ], [ 0, %else ]
ret i32 %if_exp_result.0
} }
"} "}
.trim(); .trim();

View File

@ -14,7 +14,7 @@ use crate::{
typedef::{Type, Unifier}, typedef::{Type, Unifier},
}, },
}; };
use inkwell::values::{BasicValueEnum, FloatValue, IntValue, PointerValue}; use inkwell::values::{BasicValueEnum, FloatValue, IntValue, PointerValue, StructValue};
use itertools::{chain, izip}; use itertools::{chain, izip};
use nac3parser::ast::{Expr, Location, StrRef}; use nac3parser::ast::{Expr, Location, StrRef};
use parking_lot::RwLock; use parking_lot::RwLock;
@ -29,6 +29,8 @@ pub enum SymbolValue {
Double(f64), Double(f64),
Bool(bool), Bool(bool),
Tuple(Vec<SymbolValue>), Tuple(Vec<SymbolValue>),
OptionSome(Box<SymbolValue>),
OptionNone,
} }
impl Display for SymbolValue { impl Display for SymbolValue {
@ -50,6 +52,8 @@ impl Display for SymbolValue {
SymbolValue::Tuple(t) => { SymbolValue::Tuple(t) => {
write!(f, "({})", t.iter().map(|v| format!("{}", v)).collect::<Vec<_>>().join(", ")) write!(f, "({})", t.iter().map(|v| format!("{}", v)).collect::<Vec<_>>().join(", "))
} }
SymbolValue::OptionSome(v) => write!(f, "Some({})", v),
SymbolValue::OptionNone => write!(f, "none"),
} }
} }
} }
@ -106,6 +110,12 @@ impl<'ctx> From<FloatValue<'ctx>> for ValueEnum<'ctx> {
} }
} }
impl<'ctx> From<StructValue<'ctx>> for ValueEnum<'ctx> {
fn from(v: StructValue<'ctx>) -> Self {
ValueEnum::Dynamic(v.into())
}
}
impl<'ctx> ValueEnum<'ctx> { impl<'ctx> ValueEnum<'ctx> {
pub fn to_basic_value_enum<'a>( pub fn to_basic_value_enum<'a>(
self, self,
@ -153,12 +163,11 @@ pub trait SymbolResolver {
} }
thread_local! { thread_local! {
static IDENTIFIER_ID: [StrRef; 12] = [ static IDENTIFIER_ID: [StrRef; 11] = [
"int32".into(), "int32".into(),
"int64".into(), "int64".into(),
"float".into(), "float".into(),
"bool".into(), "bool".into(),
"None".into(),
"virtual".into(), "virtual".into(),
"list".into(), "list".into(),
"tuple".into(), "tuple".into(),
@ -183,14 +192,13 @@ pub fn parse_type_annotation<T>(
let int64_id = ids[1]; let int64_id = ids[1];
let float_id = ids[2]; let float_id = ids[2];
let bool_id = ids[3]; let bool_id = ids[3];
let none_id = ids[4]; let virtual_id = ids[4];
let virtual_id = ids[5]; let list_id = ids[5];
let list_id = ids[6]; let tuple_id = ids[6];
let tuple_id = ids[7]; let str_id = ids[7];
let str_id = ids[8]; let exn_id = ids[8];
let exn_id = ids[9]; let uint32_id = ids[9];
let uint32_id = ids[10]; let uint64_id = ids[10];
let uint64_id = ids[11];
let name_handling = |id: &StrRef, loc: Location, unifier: &mut Unifier| { let name_handling = |id: &StrRef, loc: Location, unifier: &mut Unifier| {
if *id == int32_id { if *id == int32_id {
@ -205,8 +213,6 @@ pub fn parse_type_annotation<T>(
Ok(primitives.float) Ok(primitives.float)
} else if *id == bool_id { } else if *id == bool_id {
Ok(primitives.bool) Ok(primitives.bool)
} else if *id == none_id {
Ok(primitives.none)
} else if *id == str_id { } else if *id == str_id {
Ok(primitives.str) Ok(primitives.str)
} else if *id == exn_id { } else if *id == exn_id {

View File

@ -105,6 +105,20 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
("__param2__".into(), int64, true), ("__param2__".into(), int64, true),
]; ];
// for Option, is_some and is_none share the same type: () -> bool,
// and they are methods under the same class `Option`
let (is_some_ty, unwrap_ty, (option_ty_var, option_ty_var_id)) =
if let TypeEnum::TObj { fields, params, .. } =
primitives.1.get_ty(primitives.0.option).as_ref()
{
(
*fields.get(&"is_some".into()).unwrap(),
*fields.get(&"unwrap".into()).unwrap(),
(*params.iter().next().unwrap().1, *params.iter().next().unwrap().0),
)
} else {
unreachable!()
};
let top_level_def_list = vec![ let top_level_def_list = vec![
Arc::new(RwLock::new(TopLevelComposer::make_top_level_class_def( Arc::new(RwLock::new(TopLevelComposer::make_top_level_class_def(
0, 0,
@ -180,6 +194,94 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
None, None,
None, None,
))), ))),
Arc::new(RwLock::new({
TopLevelDef::Class {
name: "Option".into(),
object_id: DefinitionId(10),
type_vars: vec![option_ty_var],
fields: vec![],
methods: vec![
("is_some".into(), is_some_ty.0, DefinitionId(11)),
("is_none".into(), is_some_ty.0, DefinitionId(12)),
("unwrap".into(), unwrap_ty.0, DefinitionId(13)),
],
ancestors: vec![TypeAnnotation::CustomClass {
id: DefinitionId(10),
params: Default::default(),
}],
constructor: None,
resolver: None,
loc: None,
}
})),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "Option.is_some".into(),
simple_name: "is_some".into(),
signature: is_some_ty.0,
var_id: vec![option_ty_var_id],
instance_to_symbol: Default::default(),
instance_to_stmt: Default::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, obj, _, _, generator| {
let obj_val = obj.unwrap().1.clone().to_basic_value_enum(ctx, generator)?;
if let BasicValueEnum::PointerValue(ptr) = obj_val {
Ok(Some(ctx.builder.build_is_not_null(ptr, "is_some").into()))
} else {
unreachable!("option must be ptr")
}
},
)))),
loc: None,
})),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "Option.is_none".into(),
simple_name: "is_none".into(),
signature: is_some_ty.0,
var_id: vec![option_ty_var_id],
instance_to_symbol: Default::default(),
instance_to_stmt: Default::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, obj, _, _, generator| {
let obj_val = obj.unwrap().1.clone().to_basic_value_enum(ctx, generator)?;
if let BasicValueEnum::PointerValue(ptr) = obj_val {
Ok(Some(ctx.builder.build_is_null(ptr, "is_none").into()))
} else {
unreachable!("option must be ptr")
}
},
)))),
loc: None,
})),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "Option.unwrap".into(),
simple_name: "unwrap".into(),
signature: unwrap_ty.0,
var_id: vec![option_ty_var_id],
instance_to_symbol: Default::default(),
instance_to_stmt: Default::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, obj, _, _, generator| {
if let BasicValueEnum::PointerValue(ptr) = obj.unwrap().1.to_basic_value_enum(ctx, generator)? {
let not_null = ctx.builder.build_is_not_null(ptr, "unwrap_not_null");
ctx.make_assert(
generator,
not_null,
"0:UnwrapNoneError",
"",
[None, None, None],
ctx.current_loc,
);
Ok(Some(ctx.builder.build_load(ptr, "unwrap_some")))
} else {
unreachable!("option must be ptr")
}
},
)))),
loc: None,
})),
Arc::new(RwLock::new(TopLevelDef::Function { Arc::new(RwLock::new(TopLevelDef::Function {
name: "int32".into(), name: "int32".into(),
simple_name: "int32".into(), simple_name: "int32".into(),
@ -581,8 +683,28 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
step = Some(arg.1.clone().to_basic_value_enum(ctx, generator)?); step = Some(arg.1.clone().to_basic_value_enum(ctx, generator)?);
} }
} }
// TODO: error when step == 0 let step = match step {
let step = step.unwrap_or_else(|| int32.const_int(1, false).into()); Some(step) => {
let step = step.into_int_value();
// assert step != 0, throw exception if not
let not_zero = ctx.builder.build_int_compare(
IntPredicate::NE,
step,
step.get_type().const_zero(),
"range_step_ne",
);
ctx.make_assert(
generator,
not_zero,
"0:ValueError",
"range() step must not be zero",
[None, None, None],
ctx.current_loc,
);
step
}
None => int32.const_int(1, false),
};
let stop = stop.unwrap_or_else(|| { let stop = stop.unwrap_or_else(|| {
let v = start.unwrap(); let v = start.unwrap();
start = None; start = None;
@ -884,7 +1006,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
Ok(if ctx.unifier.unioned(arg_ty, range_ty) { Ok(if ctx.unifier.unioned(arg_ty, range_ty) {
let arg = arg.into_pointer_value(); let arg = arg.into_pointer_value();
let (start, end, step) = destructure_range(ctx, arg); let (start, end, step) = destructure_range(ctx, arg);
Some(calculate_len_for_slice_range(ctx, start, end, step).into()) Some(calculate_len_for_slice_range(generator, ctx, start, end, step).into())
} else { } else {
let int32 = ctx.ctx.i32_type(); let int32 = ctx.ctx.i32_type();
let zero = int32.const_zero(); let zero = int32.const_zero();
@ -1098,6 +1220,28 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
)))), )))),
loc: None, loc: None,
})), })),
Arc::new(RwLock::new(TopLevelDef::Function {
name: "Some".into(),
simple_name: "Some".into(),
signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![FuncArg { name: "n".into(), ty: option_ty_var, default_value: None }],
ret: primitives.0.option,
vars: HashMap::from([(option_ty_var_id, option_ty_var)]),
})),
var_id: vec![option_ty_var_id],
instance_to_symbol: Default::default(),
instance_to_stmt: Default::default(),
resolver: None,
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|ctx, _, _fun, args, generator| {
let arg_val = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
let alloca = ctx.builder.build_alloca(arg_val.get_type(), "alloca_some");
ctx.builder.build_store(alloca, arg_val);
Ok(Some(alloca.into()))
},
)))),
loc: None,
})),
]; ];
let ast_list: Vec<Option<ast::Stmt<()>>> = let ast_list: Vec<Option<ast::Stmt<()>>> =
@ -1123,6 +1267,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
"min", "min",
"max", "max",
"abs", "abs",
"Some",
], ],
) )
} }

View File

@ -74,6 +74,8 @@ impl TopLevelComposer {
"self".into(), "self".into(),
"Kernel".into(), "Kernel".into(),
"KernelInvariant".into(), "KernelInvariant".into(),
"Some".into(),
"Option".into(),
]); ]);
let defined_names: HashSet<String> = Default::default(); let defined_names: HashSet<String> = Default::default();
let method_class: HashMap<DefinitionId, DefinitionId> = Default::default(); let method_class: HashMap<DefinitionId, DefinitionId> = Default::default();
@ -89,10 +91,9 @@ impl TopLevelComposer {
assert!(name == *simple_name); assert!(name == *simple_name);
builtin_ty.insert(name, *signature); builtin_ty.insert(name, *signature);
builtin_id.insert(name, DefinitionId(id)); builtin_id.insert(name, DefinitionId(id));
} else if let TopLevelDef::Class { name, constructor, object_id, type_vars, .. } = &*def } else if let TopLevelDef::Class { name, constructor, object_id, .. } = &*def
{ {
assert!(id == object_id.0); assert!(id == object_id.0);
assert!(type_vars.is_empty());
if let Some(constructor) = constructor { if let Some(constructor) = constructor {
builtin_ty.insert(*name, *constructor); builtin_ty.insert(*name, *constructor);
} }
@ -1783,9 +1784,7 @@ impl TopLevelComposer {
}) })
}; };
let mut identifiers = { let mut identifiers = {
// NOTE: none and function args?
let mut result: HashSet<_> = HashSet::new(); let mut result: HashSet<_> = HashSet::new();
result.insert("None".into());
if self_type.is_some() { if self_type.is_some() {
result.insert("self".into()); result.insert("self".into());
} }
@ -1808,9 +1807,7 @@ impl TopLevelComposer {
}, },
unifier, unifier,
variable_mapping: { variable_mapping: {
// NOTE: none and function args?
let mut result: HashMap<StrRef, Type> = HashMap::new(); let mut result: HashMap<StrRef, Type> = HashMap::new();
result.insert("None".into(), primitives_ty.none);
if let Some(self_ty) = self_type { if let Some(self_ty) = self_type {
result.insert("self".into(), self_ty); result.insert("self".into(), self_ty);
} }

View File

@ -107,7 +107,43 @@ impl TopLevelComposer {
fields: HashMap::new(), fields: HashMap::new(),
params: HashMap::new(), params: HashMap::new(),
}); });
let primitives = PrimitiveStore { int32, int64, float, bool, none, range, str, exception, uint32, uint64 };
let option_type_var = unifier.get_fresh_var(Some("option_type_var".into()), None);
let is_some_type_fun_ty = unifier.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![],
ret: bool,
vars: HashMap::from([(option_type_var.1, option_type_var.0)]),
}));
let unwrap_fun_ty = unifier.add_ty(TypeEnum::TFunc(FunSignature {
args: vec![],
ret: option_type_var.0,
vars: HashMap::from([(option_type_var.1, option_type_var.0)]),
}));
let option = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(10),
fields: vec![
("is_some".into(), (is_some_type_fun_ty, true)),
("is_none".into(), (is_some_type_fun_ty, true)),
("unwrap".into(), (unwrap_fun_ty, true)),
]
.into_iter()
.collect::<HashMap<_, _>>(),
params: HashMap::from([(option_type_var.1, option_type_var.0)]),
});
let primitives = PrimitiveStore {
int32,
int64,
float,
bool,
none,
range,
str,
exception,
uint32,
uint64,
option,
};
crate::typecheck::magic_methods::set_primitives_magic_methods(&primitives, &mut unifier); crate::typecheck::magic_methods::set_primitives_magic_methods(&primitives, &mut unifier);
(primitives, unifier) (primitives, unifier)
} }
@ -380,76 +416,77 @@ impl TopLevelComposer {
primitive: &PrimitiveStore, primitive: &PrimitiveStore,
unifier: &mut Unifier, unifier: &mut Unifier,
) -> Result<(), String> { ) -> Result<(), String> {
let res = match val { fn type_default_param(
SymbolValue::Bool(..) => { val: &SymbolValue,
if matches!(ty, TypeAnnotation::Primitive(t) if *t == primitive.bool) { primitive: &PrimitiveStore,
None unifier: &mut Unifier,
} else { ) -> TypeAnnotation {
Some("bool".to_string()) match val {
SymbolValue::Bool(..) => TypeAnnotation::Primitive(primitive.bool),
SymbolValue::Double(..) => TypeAnnotation::Primitive(primitive.float),
SymbolValue::I32(..) => TypeAnnotation::Primitive(primitive.int32),
SymbolValue::I64(..) => TypeAnnotation::Primitive(primitive.int64),
SymbolValue::U32(..) => TypeAnnotation::Primitive(primitive.uint32),
SymbolValue::U64(..) => TypeAnnotation::Primitive(primitive.uint64),
SymbolValue::Str(..) => TypeAnnotation::Primitive(primitive.str),
SymbolValue::Tuple(vs) => {
let vs_tys = vs
.iter()
.map(|v| type_default_param(v, primitive, unifier))
.collect::<Vec<_>>();
TypeAnnotation::Tuple(vs_tys)
} }
} SymbolValue::OptionNone => TypeAnnotation::CustomClass {
SymbolValue::Double(..) => { id: primitive.option.get_obj_id(unifier),
if matches!(ty, TypeAnnotation::Primitive(t) if *t == primitive.float) { params: Default::default(),
None },
} else { SymbolValue::OptionSome(v) => {
Some("float".to_string()) let ty = type_default_param(v, primitive, unifier);
} TypeAnnotation::CustomClass {
} id: primitive.option.get_obj_id(unifier),
SymbolValue::I32(..) => { params: vec![ty],
if matches!(ty, TypeAnnotation::Primitive(t) if *t == primitive.int32) {
None
} else {
Some("int32".to_string())
}
}
SymbolValue::I64(..) => {
if matches!(ty, TypeAnnotation::Primitive(t) if *t == primitive.int64) {
None
} else {
Some("int64".to_string())
}
}
SymbolValue::U32(..) => {
if matches!(ty, TypeAnnotation::Primitive(t) if *t == primitive.uint32) {
None
} else {
Some("uint32".to_string())
}
}
SymbolValue::U64(..) => {
if matches!(ty, TypeAnnotation::Primitive(t) if *t == primitive.uint64) {
None
} else {
Some("uint64".to_string())
}
}
SymbolValue::Str(..) => {
if matches!(ty, TypeAnnotation::Primitive(t) if *t == primitive.str) {
None
} else {
Some("str".to_string())
}
}
SymbolValue::Tuple(elts) => {
if let TypeAnnotation::Tuple(elts_ty) = ty {
for (e, t) in elts.iter().zip(elts_ty.iter()) {
Self::check_default_param_type(e, t, primitive, unifier)?
} }
if elts.len() != elts_ty.len() {
Some(format!("tuple of length {}", elts.len()))
} else {
None
}
} else {
Some("tuple".to_string())
} }
} }
}; }
if let Some(found) = res {
fn is_compatible(
found: &TypeAnnotation,
expect: &TypeAnnotation,
unifier: &mut Unifier,
primitive: &PrimitiveStore,
) -> bool {
match (found, expect) {
(TypeAnnotation::Primitive(f), TypeAnnotation::Primitive(e)) => {
unifier.unioned(*f, *e)
}
(
TypeAnnotation::CustomClass { id: f_id, params: f_param },
TypeAnnotation::CustomClass { id: e_id, params: e_param },
) => {
*f_id == *e_id
&& *f_id == primitive.option.get_obj_id(unifier)
&& (f_param.is_empty()
|| (f_param.len() == 1
&& e_param.len() == 1
&& is_compatible(&f_param[0], &e_param[0], unifier, primitive)))
}
(TypeAnnotation::Tuple(f), TypeAnnotation::Tuple(e)) => {
f.len() == e.len()
&& f.iter()
.zip(e.iter())
.all(|(f, e)| is_compatible(f, e, unifier, primitive))
}
_ => false,
}
}
let found = type_default_param(val, primitive, unifier);
if !is_compatible(&found, ty, unifier, primitive) {
Err(format!( Err(format!(
"incompatible default parameter type, expect {}, found {}", "incompatible default parameter type, expect {}, found {}",
ty.stringify(unifier), ty.stringify(unifier),
found found.stringify(unifier),
)) ))
} else { } else {
Ok(()) Ok(())
@ -475,6 +512,10 @@ pub fn parse_parameter_default_value(
Constant::Tuple(tuple) => Ok(SymbolValue::Tuple( Constant::Tuple(tuple) => Ok(SymbolValue::Tuple(
tuple.iter().map(|x| handle_constant(x, loc)).collect::<Result<Vec<_>, _>>()?, tuple.iter().map(|x| handle_constant(x, loc)).collect::<Result<Vec<_>, _>>()?,
)), )),
Constant::None => Err(format!(
"`None` is not supported, use `none` for option type instead ({})",
loc
)),
_ => unimplemented!("this constant is not supported at {}", loc), _ => unimplemented!("this constant is not supported at {}", loc),
} }
} }
@ -512,6 +553,11 @@ pub fn parse_parameter_default_value(
} }
_ => Err(format!("only allow constant integer here at {}", default.location)) _ => Err(format!("only allow constant integer here at {}", default.location))
} }
ast::ExprKind::Name { id, .. } if *id == "Some".into() => Ok(
SymbolValue::OptionSome(
Box::new(parse_parameter_default_value(&args[0], resolver)?)
)
),
_ => Err(format!("unsupported default parameter at {}", default.location)), _ => Err(format!("unsupported default parameter at {}", default.location)),
} }
} }
@ -520,15 +566,20 @@ pub fn parse_parameter_default_value(
.map(|x| parse_parameter_default_value(x, resolver)) .map(|x| parse_parameter_default_value(x, resolver))
.collect::<Result<Vec<_>, _>>()? .collect::<Result<Vec<_>, _>>()?
)), )),
ast::ExprKind::Name { id, .. } if id == &"none".into() => Ok(SymbolValue::OptionNone),
ast::ExprKind::Name { id, .. } => { ast::ExprKind::Name { id, .. } => {
resolver.get_default_param_value(default).ok_or_else( resolver.get_default_param_value(default).ok_or_else(
|| format!( || format!(
"`{}` cannot be used as a default parameter at {} (not primitive type or tuple / not defined?)", "`{}` cannot be used as a default parameter at {} \
(not primitive type, option or tuple / not defined?)",
id, id,
default.location default.location
) )
) )
} }
_ => Err(format!("unsupported default parameter at {}", default.location)) _ => Err(format!(
"unsupported default parameter (not primitive type, option or tuple) at {}",
default.location
))
} }
} }

View File

@ -5,10 +5,10 @@ expression: res_vec
--- ---
[ [
"Class {\nname: \"Generic_A\",\nancestors: [\"{class: Generic_A, params: [\\\"V\\\"]}\", \"{class: B, params: []}\"],\nfields: [\"aa\", \"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"foo\", \"fn[[b:T], none]\"), (\"fun\", \"fn[[a:int32], V]\")],\ntype_vars: [\"V\"]\n}\n", "Class {\nname: \"Generic_A\",\nancestors: [\"Generic_A[V]\", \"B\"],\nfields: [\"aa\", \"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"foo\", \"fn[[b:T], none]\"), (\"fun\", \"fn[[a:int32], V]\")],\ntype_vars: [\"V\"]\n}\n",
"Function {\nname: \"Generic_A.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n", "Function {\nname: \"Generic_A.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"Generic_A.fun\",\nsig: \"fn[[a:int32], V]\",\nvar_id: [17]\n}\n", "Function {\nname: \"Generic_A.fun\",\nsig: \"fn[[a:int32], V]\",\nvar_id: [18]\n}\n",
"Class {\nname: \"B\",\nancestors: [\"{class: B, params: []}\"],\nfields: [\"aa\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"foo\", \"fn[[b:T], none]\")],\ntype_vars: []\n}\n", "Class {\nname: \"B\",\nancestors: [\"B\"],\nfields: [\"aa\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"foo\", \"fn[[b:T], none]\")],\ntype_vars: []\n}\n",
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n", "Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"B.foo\",\nsig: \"fn[[b:T], none]\",\nvar_id: []\n}\n", "Function {\nname: \"B.foo\",\nsig: \"fn[[b:T], none]\",\nvar_id: []\n}\n",
] ]

View File

@ -5,13 +5,13 @@ expression: res_vec
--- ---
[ [
"Class {\nname: \"A\",\nancestors: [\"{class: A, params: [\\\"T\\\"]}\"],\nfields: [\"a\", \"b\", \"c\"],\nmethods: [(\"__init__\", \"fn[[t:T], none]\"), (\"fun\", \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\"), (\"foo\", \"fn[[c:C], none]\")],\ntype_vars: [\"T\"]\n}\n", "Class {\nname: \"A\",\nancestors: [\"A[T]\"],\nfields: [\"a\", \"b\", \"c\"],\nmethods: [(\"__init__\", \"fn[[t:T], none]\"), (\"fun\", \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\"), (\"foo\", \"fn[[c:C], none]\")],\ntype_vars: [\"T\"]\n}\n",
"Function {\nname: \"A.__init__\",\nsig: \"fn[[t:T], none]\",\nvar_id: []\n}\n", "Function {\nname: \"A.__init__\",\nsig: \"fn[[t:T], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"A.fun\",\nsig: \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\",\nvar_id: []\n}\n", "Function {\nname: \"A.fun\",\nsig: \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\",\nvar_id: []\n}\n",
"Function {\nname: \"A.foo\",\nsig: \"fn[[c:C], none]\",\nvar_id: []\n}\n", "Function {\nname: \"A.foo\",\nsig: \"fn[[c:C], none]\",\nvar_id: []\n}\n",
"Class {\nname: \"B\",\nancestors: [\"{class: B, params: [\\\"var6\\\"]}\", \"{class: A, params: [\\\"float\\\"]}\"],\nfields: [\"a\", \"b\", \"c\", \"d\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\"), (\"foo\", \"fn[[c:C], none]\")],\ntype_vars: [\"var6\"]\n}\n", "Class {\nname: \"B\",\nancestors: [\"B[var7]\", \"A[float]\"],\nfields: [\"a\", \"b\", \"c\", \"d\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\"), (\"foo\", \"fn[[c:C], none]\")],\ntype_vars: [\"var7\"]\n}\n",
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n", "Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"B.fun\",\nsig: \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\",\nvar_id: []\n}\n", "Function {\nname: \"B.fun\",\nsig: \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\",\nvar_id: []\n}\n",
"Class {\nname: \"C\",\nancestors: [\"{class: C, params: []}\", \"{class: B, params: [\\\"bool\\\"]}\", \"{class: A, params: [\\\"float\\\"]}\"],\nfields: [\"a\", \"b\", \"c\", \"d\", \"e\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\"), (\"foo\", \"fn[[c:C], none]\")],\ntype_vars: []\n}\n", "Class {\nname: \"C\",\nancestors: [\"C\", \"B[bool]\", \"A[float]\"],\nfields: [\"a\", \"b\", \"c\", \"d\", \"e\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a:int32, b:T], list[virtual[B[bool]]]]\"), (\"foo\", \"fn[[c:C], none]\")],\ntype_vars: []\n}\n",
"Function {\nname: \"C.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n", "Function {\nname: \"C.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
] ]

View File

@ -6,10 +6,10 @@ expression: res_vec
--- ---
[ [
"Function {\nname: \"foo\",\nsig: \"fn[[a:list[int32], b:tuple[T, float]], A[B, bool]]\",\nvar_id: []\n}\n", "Function {\nname: \"foo\",\nsig: \"fn[[a:list[int32], b:tuple[T, float]], A[B, bool]]\",\nvar_id: []\n}\n",
"Class {\nname: \"A\",\nancestors: [\"{class: A, params: [\\\"T\\\", \\\"V\\\"]}\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[v:V], none]\"), (\"fun\", \"fn[[a:T], V]\")],\ntype_vars: [\"T\", \"V\"]\n}\n", "Class {\nname: \"A\",\nancestors: [\"A[T, V]\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[v:V], none]\"), (\"fun\", \"fn[[a:T], V]\")],\ntype_vars: [\"T\", \"V\"]\n}\n",
"Function {\nname: \"A.__init__\",\nsig: \"fn[[v:V], none]\",\nvar_id: [19]\n}\n", "Function {\nname: \"A.__init__\",\nsig: \"fn[[v:V], none]\",\nvar_id: [20]\n}\n",
"Function {\nname: \"A.fun\",\nsig: \"fn[[a:T], V]\",\nvar_id: [24]\n}\n", "Function {\nname: \"A.fun\",\nsig: \"fn[[a:T], V]\",\nvar_id: [25]\n}\n",
"Function {\nname: \"gfun\",\nsig: \"fn[[a:A[int32, list[float]]], none]\",\nvar_id: []\n}\n", "Function {\nname: \"gfun\",\nsig: \"fn[[a:A[int32, list[float]]], none]\",\nvar_id: []\n}\n",
"Class {\nname: \"B\",\nancestors: [\"{class: B, params: []}\"],\nfields: [],\nmethods: [(\"__init__\", \"fn[[], none]\")],\ntype_vars: []\n}\n", "Class {\nname: \"B\",\nancestors: [\"B\"],\nfields: [],\nmethods: [(\"__init__\", \"fn[[], none]\")],\ntype_vars: []\n}\n",
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n", "Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
] ]

View File

@ -5,10 +5,10 @@ expression: res_vec
--- ---
[ [
"Class {\nname: \"A\",\nancestors: [\"{class: A, params: [\\\"var5\\\", \\\"var6\\\"]}\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[a:A[bool, float], b:B], none]\"), (\"fun\", \"fn[[a:A[bool, float]], A[bool, int32]]\")],\ntype_vars: [\"var5\", \"var6\"]\n}\n", "Class {\nname: \"A\",\nancestors: [\"A[var6, var7]\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[a:A[bool, float], b:B], none]\"), (\"fun\", \"fn[[a:A[bool, float]], A[bool, int32]]\")],\ntype_vars: [\"var6\", \"var7\"]\n}\n",
"Function {\nname: \"A.__init__\",\nsig: \"fn[[a:A[bool, float], b:B], none]\",\nvar_id: []\n}\n", "Function {\nname: \"A.__init__\",\nsig: \"fn[[a:A[bool, float], b:B], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"A.fun\",\nsig: \"fn[[a:A[bool, float]], A[bool, int32]]\",\nvar_id: []\n}\n", "Function {\nname: \"A.fun\",\nsig: \"fn[[a:A[bool, float]], A[bool, int32]]\",\nvar_id: []\n}\n",
"Class {\nname: \"B\",\nancestors: [\"{class: B, params: []}\", \"{class: A, params: [\\\"int64\\\", \\\"bool\\\"]}\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a:A[bool, float]], A[bool, int32]]\"), (\"foo\", \"fn[[b:B], B]\"), (\"bar\", \"fn[[a:A[int32, list[B]]], tuple[A[bool, virtual[A[B, int32]]], B]]\")],\ntype_vars: []\n}\n", "Class {\nname: \"B\",\nancestors: [\"B\", \"A[int64, bool]\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[a:A[bool, float]], A[bool, int32]]\"), (\"foo\", \"fn[[b:B], B]\"), (\"bar\", \"fn[[a:A[int32, list[B]]], tuple[A[bool, virtual[A[B, int32]]], B]]\")],\ntype_vars: []\n}\n",
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n", "Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"B.foo\",\nsig: \"fn[[b:B], B]\",\nvar_id: []\n}\n", "Function {\nname: \"B.foo\",\nsig: \"fn[[b:B], B]\",\nvar_id: []\n}\n",
"Function {\nname: \"B.bar\",\nsig: \"fn[[a:A[int32, list[B]]], tuple[A[bool, virtual[A[B, int32]]], B]]\",\nvar_id: []\n}\n", "Function {\nname: \"B.bar\",\nsig: \"fn[[a:A[int32, list[B]]], tuple[A[bool, virtual[A[B, int32]]], B]]\",\nvar_id: []\n}\n",

View File

@ -1,19 +1,19 @@
--- ---
source: nac3core/src/toplevel/test.rs source: nac3core/src/toplevel/test.rs
assertion_line: 540 assertion_line: 549
expression: res_vec expression: res_vec
--- ---
[ [
"Class {\nname: \"A\",\nancestors: [\"{class: A, params: []}\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b:B], none]\"), (\"foo\", \"fn[[a:T, b:V], none]\")],\ntype_vars: []\n}\n", "Class {\nname: \"A\",\nancestors: [\"A\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b:B], none]\"), (\"foo\", \"fn[[a:T, b:V], none]\")],\ntype_vars: []\n}\n",
"Function {\nname: \"A.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n", "Function {\nname: \"A.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"A.fun\",\nsig: \"fn[[b:B], none]\",\nvar_id: []\n}\n", "Function {\nname: \"A.fun\",\nsig: \"fn[[b:B], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"A.foo\",\nsig: \"fn[[a:T, b:V], none]\",\nvar_id: [25]\n}\n", "Function {\nname: \"A.foo\",\nsig: \"fn[[a:T, b:V], none]\",\nvar_id: [26]\n}\n",
"Class {\nname: \"B\",\nancestors: [\"{class: B, params: []}\", \"{class: C, params: []}\", \"{class: A, params: []}\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b:B], none]\"), (\"foo\", \"fn[[a:T, b:V], none]\")],\ntype_vars: []\n}\n", "Class {\nname: \"B\",\nancestors: [\"B\", \"C\", \"A\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b:B], none]\"), (\"foo\", \"fn[[a:T, b:V], none]\")],\ntype_vars: []\n}\n",
"Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n", "Function {\nname: \"B.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
"Class {\nname: \"C\",\nancestors: [\"{class: C, params: []}\", \"{class: A, params: []}\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b:B], none]\"), (\"foo\", \"fn[[a:T, b:V], none]\")],\ntype_vars: []\n}\n", "Class {\nname: \"C\",\nancestors: [\"C\", \"A\"],\nfields: [\"a\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"fun\", \"fn[[b:B], none]\"), (\"foo\", \"fn[[a:T, b:V], none]\")],\ntype_vars: []\n}\n",
"Function {\nname: \"C.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n", "Function {\nname: \"C.__init__\",\nsig: \"fn[[], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"C.fun\",\nsig: \"fn[[b:B], none]\",\nvar_id: []\n}\n", "Function {\nname: \"C.fun\",\nsig: \"fn[[b:B], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"foo\",\nsig: \"fn[[a:A], none]\",\nvar_id: []\n}\n", "Function {\nname: \"foo\",\nsig: \"fn[[a:A], none]\",\nvar_id: []\n}\n",
"Function {\nname: \"ff\",\nsig: \"fn[[a:T], V]\",\nvar_id: [33]\n}\n", "Function {\nname: \"ff\",\nsig: \"fn[[a:T], V]\",\nvar_id: [34]\n}\n",
] ]

View File

@ -1,9 +1,9 @@
--- ---
source: nac3core/src/toplevel/test.rs source: nac3core/src/toplevel/test.rs
assertion_line: 541 assertion_line: 549
expression: res_vec expression: res_vec
--- ---
[ [
"Class {\nname: \"A\",\nancestors: [\"{class: A, params: []}\"],\nfields: [],\nmethods: [],\ntype_vars: []\n}\n", "Class {\nname: \"A\",\nancestors: [\"A\"],\nfields: [],\nmethods: [],\ntype_vars: []\n}\n",
] ]

View File

@ -65,14 +65,14 @@ impl SymbolResolver for Resolver {
} }
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> { fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> {
self.0.id_to_def.lock().get(&id).cloned().ok_or("Unknown identifier".to_string()) self.0.id_to_def.lock().get(&id).cloned().ok_or_else(|| "Unknown identifier".to_string())
} }
fn get_string_id(&self, _: &str) -> i32 { fn get_string_id(&self, _: &str) -> i32 {
unimplemented!() unimplemented!()
} }
fn get_exception_id(&self, tyid: usize) -> usize { fn get_exception_id(&self, _tyid: usize) -> usize {
unimplemented!() unimplemented!()
} }
} }

View File

@ -29,20 +29,28 @@ impl TypeAnnotation {
{ {
(*name).into() (*name).into()
} else { } else {
format!("def_{}", id.0) unreachable!()
} }
} }
None => format!("def_{}", id.0), None => format!("class_def_{}", id.0),
}; };
format!( format!(
"{{class: {}, params: {:?}}}", "{}{}",
class_name, class_name,
params.iter().map(|p| p.stringify(unifier)).collect_vec() {
let param_list = params.iter().map(|p| p.stringify(unifier)).collect_vec().join(", ");
if param_list.is_empty() {
"".into()
} else {
format!("[{}]", param_list)
}
}
) )
} }
Virtual(ty) | List(ty) => ty.stringify(unifier), Virtual(ty) => format!("virtual[{}]", ty.stringify(unifier)),
List(ty) => format!("list[{}]", ty.stringify(unifier)),
Tuple(types) => { Tuple(types) => {
format!("({:?})", types.iter().map(|p| p.stringify(unifier)).collect_vec()) format!("tuple[{}]", types.iter().map(|p| p.stringify(unifier)).collect_vec().join(", "))
} }
} }
} }
@ -72,8 +80,6 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
Ok(TypeAnnotation::Primitive(primitives.float)) Ok(TypeAnnotation::Primitive(primitives.float))
} else if id == &"bool".into() { } else if id == &"bool".into() {
Ok(TypeAnnotation::Primitive(primitives.bool)) Ok(TypeAnnotation::Primitive(primitives.bool))
} else if id == &"None".into() {
Ok(TypeAnnotation::Primitive(primitives.none))
} else if id == &"str".into() { } else if id == &"str".into() {
Ok(TypeAnnotation::Primitive(primitives.str)) Ok(TypeAnnotation::Primitive(primitives.str))
} else if id == &"Exception".into() { } else if id == &"Exception".into() {
@ -223,6 +229,29 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
Ok(TypeAnnotation::List(def_ann.into())) Ok(TypeAnnotation::List(def_ann.into()))
} }
// option
ast::ExprKind::Subscript { value, slice, .. }
if {
matches!(&value.node, ast::ExprKind::Name { id, .. } if id == &"Option".into())
} =>
{
let def_ann = parse_ast_to_type_annotation_kinds(
resolver,
top_level_defs,
unifier,
primitives,
slice.as_ref(),
locked,
)?;
let id =
if let TypeEnum::TObj { obj_id, .. } = unifier.get_ty(primitives.option).as_ref() {
*obj_id
} else {
unreachable!()
};
Ok(TypeAnnotation::CustomClass { id, params: vec![def_ann] })
}
// tuple // tuple
ast::ExprKind::Subscript { value, slice, .. } ast::ExprKind::Subscript { value, slice, .. }
if { if {

View File

@ -20,6 +20,8 @@ impl<'a> Inferencer<'a> {
defined_identifiers: &mut HashSet<StrRef>, defined_identifiers: &mut HashSet<StrRef>,
) -> Result<(), String> { ) -> Result<(), String> {
match &pattern.node { match &pattern.node {
ast::ExprKind::Name { id, .. } if id == &"none".into() =>
Err(format!("cannot assign to a `none` (at {})", pattern.location)),
ExprKind::Name { id, .. } => { ExprKind::Name { id, .. } => {
if !defined_identifiers.contains(id) { if !defined_identifiers.contains(id) {
defined_identifiers.insert(*id); defined_identifiers.insert(*id);
@ -70,6 +72,9 @@ impl<'a> Inferencer<'a> {
} }
match &expr.node { match &expr.node {
ExprKind::Name { id, .. } => { ExprKind::Name { id, .. } => {
if id == &"none".into() {
return Ok(());
}
self.should_have_value(expr)?; self.should_have_value(expr)?;
if !defined_identifiers.contains(id) { if !defined_identifiers.contains(id) {
match self.function_data.resolver.get_symbol_type( match self.function_data.resolver.get_symbol_type(

View File

@ -40,6 +40,7 @@ pub struct PrimitiveStore {
pub range: Type, pub range: Type,
pub str: Type, pub str: Type,
pub exception: Type, pub exception: Type,
pub option: Type,
} }
pub struct FunctionData { pub struct FunctionData {
@ -425,6 +426,13 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
let res_ty = self.infer_bin_ops(stmt.location, target, op, value)?; let res_ty = self.infer_bin_ops(stmt.location, target, op, value)?;
self.unify(res_ty, target.custom.unwrap(), &stmt.location)?; self.unify(res_ty, target.custom.unwrap(), &stmt.location)?;
} }
ast::StmtKind::Assert { test, msg, .. } => {
self.unify(test.custom.unwrap(), self.primitives.bool, &test.location)?;
match msg {
Some(m) => self.unify(m.custom.unwrap(), self.primitives.str, &m.location)?,
None => ()
}
}
_ => return report_error("Unsupported statement type", stmt.location), _ => return report_error("Unsupported statement type", stmt.location),
}; };
Ok(stmt) Ok(stmt)
@ -448,25 +456,47 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
Some(self.infer_constant(value, &expr.location)?) Some(self.infer_constant(value, &expr.location)?)
} }
ast::ExprKind::Name { id, .. } => { ast::ExprKind::Name { id, .. } => {
if !self.defined_identifiers.contains(id) { // the name `none` is special since it may have different types
match self.function_data.resolver.get_symbol_type( if id == &"none".into() {
self.unifier, if let TypeEnum::TObj { params, .. } =
&self.top_level.definitions.read(), self.unifier.get_ty_immutable(self.primitives.option).as_ref()
self.primitives, {
*id, let var_map = params
) { .iter()
Ok(_) => { .map(|(id_var, ty)| {
self.defined_identifiers.insert(*id); if let TypeEnum::TVar { id, range, name, loc, .. } = &*self.unifier.get_ty(*ty) {
} assert_eq!(*id, *id_var);
Err(e) => { (*id, self.unifier.get_fresh_var_with_range(range, *name, *loc).0)
return report_error( } else {
&format!("type error at identifier `{}` ({})", id, e), unreachable!()
expr.location, }
); })
.collect::<HashMap<_, _>>();
Some(self.unifier.subst(self.primitives.option, &var_map).unwrap())
} else {
unreachable!("must be tobj")
}
} else {
if !self.defined_identifiers.contains(id) {
match self.function_data.resolver.get_symbol_type(
self.unifier,
&self.top_level.definitions.read(),
self.primitives,
*id,
) {
Ok(_) => {
self.defined_identifiers.insert(*id);
}
Err(e) => {
return report_error(
&format!("type error at identifier `{}` ({})", id, e),
expr.location,
);
}
} }
} }
Some(self.infer_identifier(*id)?)
} }
Some(self.infer_identifier(*id)?)
} }
ast::ExprKind::List { elts, .. } => Some(self.infer_list(elts)?), ast::ExprKind::List { elts, .. } => Some(self.infer_list(elts)?),
ast::ExprKind::Tuple { elts, .. } => Some(self.infer_tuple(elts)?), ast::ExprKind::Tuple { elts, .. } => Some(self.infer_tuple(elts)?),
@ -932,6 +962,8 @@ impl<'a> Inferencer<'a> {
Ok(self.unifier.add_ty(TypeEnum::TTuple { ty: ty? })) Ok(self.unifier.add_ty(TypeEnum::TTuple { ty: ty? }))
} }
ast::Constant::Str(_) => Ok(self.primitives.str), ast::Constant::Str(_) => Ok(self.primitives.str),
ast::Constant::None
=> report_error("CPython `None` not supported (nac3 uses `none` instead)", *loc),
_ => report_error("not supported", *loc), _ => report_error("not supported", *loc),
} }
} }

View File

@ -44,14 +44,14 @@ impl SymbolResolver for Resolver {
} }
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> { fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, String> {
self.id_to_def.get(&id).cloned().ok_or("Unknown identifier".to_string()) self.id_to_def.get(&id).cloned().ok_or_else(|| "Unknown identifier".to_string())
} }
fn get_string_id(&self, _: &str) -> i32 { fn get_string_id(&self, _: &str) -> i32 {
unimplemented!() unimplemented!()
} }
fn get_exception_id(&self, tyid: usize) -> usize { fn get_exception_id(&self, _tyid: usize) -> usize {
unimplemented!() unimplemented!()
} }
} }
@ -129,7 +129,24 @@ impl TestEnvironment {
fields: HashMap::new(), fields: HashMap::new(),
params: HashMap::new(), params: HashMap::new(),
}); });
let primitives = PrimitiveStore { int32, int64, float, bool, none, range, str, exception, uint32, uint64 }; let option = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(10),
fields: HashMap::new(),
params: HashMap::new(),
});
let primitives = PrimitiveStore {
int32,
int64,
float,
bool,
none,
range,
str,
exception,
uint32,
uint64,
option,
};
set_primitives_magic_methods(&primitives, &mut unifier); set_primitives_magic_methods(&primitives, &mut unifier);
let id_to_name = [ let id_to_name = [
@ -237,6 +254,11 @@ impl TestEnvironment {
fields: HashMap::new(), fields: HashMap::new(),
params: HashMap::new(), params: HashMap::new(),
}); });
let option = unifier.add_ty(TypeEnum::TObj {
obj_id: DefinitionId(10),
fields: HashMap::new(),
params: HashMap::new(),
});
identifier_mapping.insert("None".into(), none); identifier_mapping.insert("None".into(), none);
for (i, name) in ["int32", "int64", "float", "bool", "none", "range", "str", "Exception"] for (i, name) in ["int32", "int64", "float", "bool", "none", "range", "str", "Exception"]
.iter() .iter()
@ -259,7 +281,19 @@ impl TestEnvironment {
} }
let defs = 7; let defs = 7;
let primitives = PrimitiveStore { int32, int64, float, bool, none, range, str, exception, uint32, uint64 }; let primitives = PrimitiveStore {
int32,
int64,
float,
bool,
none,
range,
str,
exception,
uint32,
uint64,
option,
};
let (v0, id) = unifier.get_dummy_var(); let (v0, id) = unifier.get_dummy_var();

View File

@ -54,6 +54,18 @@ pub enum RecordKey {
Int(i32), Int(i32),
} }
impl Type {
// a wrapper function for cleaner code so that we don't need to
// write this long pattern matching just to get the field `obj_id`
pub fn get_obj_id(self, unifier: &Unifier) -> DefinitionId {
if let TypeEnum::TObj { obj_id, .. } = unifier.get_ty_immutable(self).as_ref() {
*obj_id
} else {
unreachable!("expect a object type")
}
}
}
impl From<&RecordKey> for StrRef { impl From<&RecordKey> for StrRef {
fn from(r: &RecordKey) -> Self { fn from(r: &RecordKey) -> Self {
match r { match r {

View File

@ -121,7 +121,7 @@ mod tests {
#[test] #[test]
fn test_parse_lambda() { fn test_parse_lambda() {
let source = "lambda x, y: x * y"; // lambda(x, y): x * y"; let source = "lambda x, y: x * y"; // lambda(x, y): x * y";
let parse_ast = parse_program(&source, Default::default()).unwrap(); let parse_ast = parse_program(source, Default::default()).unwrap();
insta::assert_debug_snapshot!(parse_ast); insta::assert_debug_snapshot!(parse_ast);
} }
@ -129,7 +129,7 @@ mod tests {
fn test_parse_tuples() { fn test_parse_tuples() {
let source = "a, b = 4, 5"; let source = "a, b = 4, 5";
insta::assert_debug_snapshot!(parse_program(&source, Default::default()).unwrap()); insta::assert_debug_snapshot!(parse_program(source, Default::default()).unwrap());
} }
#[test] #[test]
@ -140,7 +140,7 @@ class Foo(A, B):
pass pass
def method_with_default(self, arg='default'): def method_with_default(self, arg='default'):
pass"; pass";
insta::assert_debug_snapshot!(parse_program(&source, Default::default()).unwrap()); insta::assert_debug_snapshot!(parse_program(source, Default::default()).unwrap());
} }
#[test] #[test]
@ -183,7 +183,7 @@ while i < 2: # nac3: 4
# nac3: if1 # nac3: if1
if 1: # nac3: if2 if 1: # nac3: if2
3"; 3";
insta::assert_debug_snapshot!(parse_program(&source, Default::default()).unwrap()); insta::assert_debug_snapshot!(parse_program(source, Default::default()).unwrap());
} }
#[test] #[test]
@ -196,7 +196,7 @@ while test: # nac3: while3
# nac3: simple assign0 # nac3: simple assign0
a = 3 # nac3: simple assign1 a = 3 # nac3: simple assign1
"; ";
insta::assert_debug_snapshot!(parse_program(&source, Default::default()).unwrap()); insta::assert_debug_snapshot!(parse_program(source, Default::default()).unwrap());
} }
#[test] #[test]
@ -215,7 +215,7 @@ if a: # nac3: small2
for i in a: # nac3: for1 for i in a: # nac3: for1
pass pass
"; ";
insta::assert_debug_snapshot!(parse_program(&source, Default::default()).unwrap()); insta::assert_debug_snapshot!(parse_program(source, Default::default()).unwrap());
} }
#[test] #[test]
@ -224,6 +224,6 @@ for i in a: # nac3: for1
if a: # nac3: something if a: # nac3: something
a = 3 a = 3
"; ";
assert!(parse_program(&source, Default::default()).is_err()); assert!(parse_program(source, Default::default()).is_err());
} }
} }

View File

@ -74,6 +74,11 @@ pub extern "C" fn __nac3_personality(_state: u32, _exception_object: u32, _conte
unimplemented!(); unimplemented!();
} }
#[no_mangle]
pub extern "C" fn __nac3_raise(_state: u32, _exception_object: u32, _context: u32) -> u32 {
unimplemented!();
}
extern "C" { extern "C" {
fn run() -> i32; fn run() -> i32;
} }

View File

@ -8,6 +8,38 @@ import pathlib
from numpy import int32, int64, uint32, uint64 from numpy import int32, int64, uint32, uint64
from typing import TypeVar, Generic from typing import TypeVar, Generic
T = TypeVar('T')
class Option(Generic[T]):
_nac3_option: T
def __init__(self, v: T):
self._nac3_option = v
def is_none(self):
return self._nac3_option is None
def is_some(self):
return not self.is_none()
def unwrap(self):
return self._nac3_option
def __repr__(self) -> str:
if self.is_none():
return "none"
else:
return "Some({})".format(repr(self._nac3_option))
def __str__(self) -> str:
if self.is_none():
return "none"
else:
return "Some({})".format(str(self._nac3_option))
def Some(v: T) -> Option[T]:
return Option(v)
none = Option(None)
def patch(module): def patch(module):
def output_asciiart(x): def output_asciiart(x):
@ -39,6 +71,9 @@ def patch(module):
module.TypeVar = TypeVar module.TypeVar = TypeVar
module.Generic = Generic module.Generic = Generic
module.extern = extern module.extern = extern
module.Option = Option
module.Some = Some
module.none = none
def file_import(filename, prefix="file_import_"): def file_import(filename, prefix="file_import_"):

View File

@ -0,0 +1,43 @@
@extern
def output_int32(x: int32):
...
def f1(a: int32 = 4):
output_int32(a)
def f2(a: int64 = int64(123)):
output_int32(int32(a))
def f3(a: uint32 = uint32(234)):
output_int32(int32(a))
def f4(a: tuple[int32, tuple[int32, int32], int64] = (4, (5, 6), int64(7))):
output_int32(a[0])
output_int32(a[1][0])
output_int32(a[1][1])
output_int32(int32(a[2]))
def f5(a: float = 3.45):
output_int32(int32(a))
def f6(a: Option[list[int32]] = none):
if a.is_none():
a = Some([11,22,33])
output_int32(a.unwrap()[2])
def f7(a: Option[tuple[int32, int64]] = Some((3, int64(123)))):
if a.is_some():
a_unwrap = a.unwrap()
output_int32(a_unwrap[0])
output_int32(int32(a_unwrap[1]))
def run() -> int32:
f1()
f2()
f3()
f4()
f5()
f6()
f7()
return 0

View File

@ -0,0 +1,9 @@
@extern
def output_int32(x: int32):
...
def run() -> int32:
for _ in range(10):
output_int32(_)
_ = 0
return 0

View File

@ -0,0 +1,40 @@
@extern
def output_int32(x: int32):
...
class A:
d: Option[int32]
e: Option[Option[int32]]
def __init__(self, a: Option[int32], b: Option[Option[int32]]):
self.d = a
self.e = b
def run() -> int32:
a = Some(3)
if a.is_some():
d = a.unwrap()
output_int32(a.unwrap())
a = none
if a.is_none():
output_int32(d + 2)
else:
a = Some(5)
c = Some(6)
output_int32(a.unwrap() + c.unwrap())
f = Some(4.3)
output_int32(int32(f.unwrap()))
obj = A(Some(6), none)
output_int32(obj.d.unwrap())
obj2 = Some(A(Some(7), none))
output_int32(obj2.unwrap().d.unwrap())
obj3 = Some(A(Some(8), Some(none)))
if obj3.unwrap().e.unwrap().is_none():
obj3.unwrap().e = Some(Some(9))
output_int32(obj3.unwrap().d.unwrap())
output_int32(obj3.unwrap().e.unwrap().unwrap())
return 0

View File

@ -0,0 +1,26 @@
@extern
def output_float64(f: float):
...
def run() -> int32:
output_float64(float(3 ** 1))
output_float64(float(3 ** 0))
output_float64(float(3 ** 19))
output_float64(1.0 ** -100)
output_float64(1.0 ** -2)
output_float64(1.0 ** 0)
output_float64(1.0 ** 1)
output_float64(1.0 ** 100)
output_float64(3.0 ** 0)
output_float64(3.0 ** 1)
output_float64(3.0 ** 2)
output_float64(3.0 ** -1)
output_float64(3.0 ** -2)
output_float64(3.0 ** -32767)
output_float64(3.0 ** -3.0)
output_float64(3.0 ** -0.0)
output_float64(3.0 ** 0.0)
output_float64(4.0 ** 0.5)
output_float64(4.0 ** -0.5)
return 0

View File

@ -0,0 +1,29 @@
@extern
def output_int32_list(x: list[int32]):
...
@extern
def output_int32(x: int32):
...
class A:
a: int32
b: bool
def __init__(self, a: int32, b: bool):
self.a = a
self.b = b
def run() -> int32:
data = [0, 1, 2, 3]
t = [(d, d + d) for d in data]
for i in t:
tt = (Some(i[1]), i[0])
tl = ([i[0], i[1] + i[0]], i[1])
output_int32(tt[0].unwrap())
output_int32(tt[1])
output_int32(tl[0][0])
output_int32(tl[0][1])
output_int32(tl[1])
return 0

26
nix/windows/PKGBUILD Normal file
View File

@ -0,0 +1,26 @@
pkgbase="mingw-w64-nac3artiq"
pkgname="mingw-w64-x86_64-nac3artiq"
pkgver=1.0
pkgrel=1
pkgdesc="New ARTIQ compiler 3"
arch=("any")
mingw_arch=("mingw64")
url="https://m-labs.hk"
license=("LGPL")
source=("nac3artiq.pyd")
noextract=("nac3artiq.pyd")
sha256sums=("SKIP")
depends=("mingw-w64-x86_64-python")
prepare() {
true
}
build() {
true
}
package() {
mkdir -p $pkgdir/mingw64/lib/python3.9/site-packages
cp ${srcdir}/nac3artiq.pyd $pkgdir/mingw64/lib/python3.9/site-packages
}

View File

@ -33,13 +33,14 @@ let
}; };
in rec { in rec {
llvm-nac3 = pkgs.stdenvNoCC.mkDerivation rec { llvm-nac3 = pkgs.stdenvNoCC.mkDerivation rec {
name = "llvm-nac3-msys2"; pname = "llvm-nac3-msys2";
version = "13.0.1";
src-llvm = pkgs.fetchurl { src-llvm = pkgs.fetchurl {
url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-13.0.1/llvm-13.0.1.src.tar.xz"; url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-${version}/llvm-${version}.src.tar.xz";
sha256 = "sha256-7GuA2Cw4SsrS3BkpA6bPLNuv+4ibhL+5janXHmMPyDQ="; sha256 = "sha256-7GuA2Cw4SsrS3BkpA6bPLNuv+4ibhL+5janXHmMPyDQ=";
}; };
src-clang = pkgs.fetchurl { src-clang = pkgs.fetchurl {
url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-13.0.1/clang-13.0.1.src.tar.xz"; url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-${version}/clang-${version}.src.tar.xz";
sha256 = "sha256-eHqeLZn1yHIKoXc+S+AJRhzTDTvUD90kWR5HNGfJF8k="; sha256 = "sha256-eHqeLZn1yHIKoXc+S+AJRhzTDTvUD90kWR5HNGfJF8k=";
}; };
buildInputs = [ pkgs.wineWowPackages.stable ]; buildInputs = [ pkgs.wineWowPackages.stable ];
@ -73,12 +74,13 @@ in rec {
'' ''
wine64 ninja install wine64 ninja install
''; '';
dontFixup = true;
}; };
nac3artiq = pkgs.rustPlatform.buildRustPackage { nac3artiq = pkgs.rustPlatform.buildRustPackage {
name = "nac3artiq"; name = "nac3artiq-msys2";
src = ../.; src = ../../.;
cargoLock = { lockFile = ../Cargo.lock; }; cargoLock = { lockFile = ../../Cargo.lock; };
nativeBuildInputs = [ pkgs.wineWowPackages.stable pkgs.zip ]; nativeBuildInputs = [ pkgs.wineWowPackages.stable ];
buildPhase = buildPhase =
'' ''
export HOME=`mktemp -d` export HOME=`mktemp -d`
@ -90,10 +92,9 @@ in rec {
''; '';
installPhase = installPhase =
'' ''
mkdir -p $out $out/nix-support mkdir $out $out/nix-support
ln -s target/release/nac3artiq.dll nac3artiq.pyd cp target/release/nac3artiq.dll $out/nac3artiq.pyd
zip $out/nac3artiq.zip nac3artiq.pyd echo file binary-dist $out/nac3artiq.pyd >> $out/nix-support/hydra-build-products
echo file binary-dist $out/nac3artiq.zip >> $out/nix-support/hydra-build-products
''; '';
checkPhase = checkPhase =
'' ''
@ -101,6 +102,56 @@ in rec {
''; '';
dontFixup = true; dontFixup = true;
}; };
nac3artiq-pkg = pkgs.stdenvNoCC.mkDerivation {
name = "nac3artiq-msys2-pkg";
nativeBuildInputs = [ pkgs.pacman pkgs.fakeroot pkgs.libarchive pkgs.zstd ];
src = nac3artiq;
phases = [ "buildPhase" "installPhase" ];
buildPhase =
''
ln -s ${./PKGBUILD} PKGBUILD
ln -s $src/nac3artiq.pyd nac3artiq.pyd
makepkg --config ${./makepkg.conf} --nodeps
'';
installPhase =
''
mkdir $out $out/nix-support
cp *.pkg.tar.zst $out
echo file msys2 $out/*.pkg.tar.zst >> $out/nix-support/hydra-build-products
'';
};
lld = pkgs.stdenvNoCC.mkDerivation rec {
pname = "lld-msys2";
version = "13.0.1";
src = pkgs.fetchurl {
url = "https://github.com/llvm/llvm-project/releases/download/llvmorg-${version}/lld-${version}.src.tar.xz";
sha256 = "sha256-Zmr3Rei/e2gFM7TRi3ox3HyrV1sebk0mGSK7r9lkTPs=";
};
buildInputs = [ pkgs.wineWowPackages.stable ];
phases = [ "unpackPhase" "patchPhase" "configurePhase" "buildPhase" "installPhase" ];
patches = [ ./lld-disable-macho.diff ];
configurePhase =
''
export HOME=`mktemp -d`
export WINEDEBUG=-all
export WINEPATH=Z:${msys2-env}/mingw64/bin\;Z:${llvm-nac3}/bin
${silenceFontconfig}
mkdir build
cd build
wine64 cmake .. -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=Z:$out
'';
buildPhase =
''
wine64 ninja
'';
installPhase =
''
mkdir -p $out $out/nix-support
cp bin/ld.lld.exe $out
echo file binary-dist $out/ld.lld.exe >> $out/nix-support/hydra-build-products
'';
dontFixup = true;
};
wine-msys2 = pkgs.writeShellScriptBin "wine-msys2" wine-msys2 = pkgs.writeShellScriptBin "wine-msys2"
'' ''
export WINEDEBUG=-all export WINEDEBUG=-all
@ -108,4 +159,12 @@ in rec {
export PYO3_CONFIG_FILE=Z:${pyo3-mingw-config} export PYO3_CONFIG_FILE=Z:${pyo3-mingw-config}
exec ${pkgs.wineWowPackages.stable}/bin/wine64 cmd exec ${pkgs.wineWowPackages.stable}/bin/wine64 cmd
''; '';
wine-msys2-build = pkgs.writeShellScriptBin "wine-msys2-build"
''
export HOME=`mktemp -d`
export WINEDEBUG=-all
export WINEPATH=Z:${msys2-env}/mingw64/bin
${silenceFontconfig}
exec ${pkgs.wineWowPackages.stable}/bin/wine64 $@
'';
} }

View File

@ -0,0 +1,36 @@
diff '--color=auto' -Naur lld-13.0.1.src/CMakeLists.txt lld-13.0.1.src-new/CMakeLists.txt
--- lld-13.0.1.src/CMakeLists.txt 2022-01-21 05:31:59.000000000 +0800
+++ lld-13.0.1.src-new/CMakeLists.txt 2022-03-27 18:26:30.284921982 +0800
@@ -206,7 +206,6 @@
add_subdirectory(docs)
add_subdirectory(COFF)
add_subdirectory(ELF)
-add_subdirectory(MachO)
add_subdirectory(MinGW)
add_subdirectory(wasm)
diff '--color=auto' -Naur lld-13.0.1.src/tools/lld/CMakeLists.txt lld-13.0.1.src-new/tools/lld/CMakeLists.txt
--- lld-13.0.1.src/tools/lld/CMakeLists.txt 2022-01-21 05:31:59.000000000 +0800
+++ lld-13.0.1.src-new/tools/lld/CMakeLists.txt 2022-03-27 18:26:40.805046295 +0800
@@ -15,7 +15,6 @@
lldCOFF
lldDriver
lldELF
- lldMachO2
lldMinGW
lldWasm
)
diff '--color=auto' -Naur lld-13.0.1.src/tools/lld/lld.cpp lld-13.0.1.src-new/tools/lld/lld.cpp
--- lld-13.0.1.src/tools/lld/lld.cpp 2022-01-21 05:31:59.000000000 +0800
+++ lld-13.0.1.src-new/tools/lld/lld.cpp 2022-03-27 08:43:54.205524156 +0800
@@ -148,10 +148,6 @@
return !elf::link(args, exitEarly, stdoutOS, stderrOS);
case WinLink:
return !coff::link(args, exitEarly, stdoutOS, stderrOS);
- case Darwin:
- return !macho::link(args, exitEarly, stdoutOS, stderrOS);
- case DarwinOld:
- return !mach_o::link(args, exitEarly, stdoutOS, stderrOS);
case Wasm:
return !lld::wasm::link(args, exitEarly, stdoutOS, stderrOS);
default:

View File

@ -10,7 +10,7 @@ curl -L https://mirror.msys2.org/msys/x86_64/pacman-mirrors-20220205-1-any.pkg.t
curl -L https://raw.githubusercontent.com/msys2/MSYS2-packages/master/pacman/pacman.conf | grep -v SigLevel | sed s\|/etc/pacman.d\|$MSYS2DIR/etc/pacman.d\|g > $MSYS2DIR/etc/pacman.conf curl -L https://raw.githubusercontent.com/msys2/MSYS2-packages/master/pacman/pacman.conf | grep -v SigLevel | sed s\|/etc/pacman.d\|$MSYS2DIR/etc/pacman.d\|g > $MSYS2DIR/etc/pacman.conf
fakeroot pacman --root $MSYS2DIR --config $MSYS2DIR/etc/pacman.conf -Syy fakeroot pacman --root $MSYS2DIR --config $MSYS2DIR/etc/pacman.conf -Syy
pacman --root $MSYS2DIR --config $MSYS2DIR/etc/pacman.conf --cachedir $MSYS2DIR/msys/cache -Sp mingw-w64-x86_64-rust mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-python3.9 mingw-w64-x86_64-python-numpy > $MSYS2DIR/packages.txt pacman --root $MSYS2DIR --config $MSYS2DIR/etc/pacman.conf --cachedir $MSYS2DIR/msys/cache -Sp mingw-w64-x86_64-rust mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-python3.9 mingw-w64-x86_64-python-numpy mingw-w64-x86_64-python-setuptools > $MSYS2DIR/packages.txt
echo "{ pkgs } : [" > msys2_packages.nix echo "{ pkgs } : [" > msys2_packages.nix
while read package; do while read package; do

2
nix/windows/makepkg.conf Normal file
View File

@ -0,0 +1,2 @@
PKGEXT='.pkg.tar.zst'
SRCEXT='.src.tar.gz'

View File

@ -6,8 +6,8 @@
}) })
(pkgs.fetchurl { (pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-zlib-1.2.11-9-any.pkg.tar.zst"; url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-zlib-1.2.12-1-any.pkg.tar.zst";
sha256 = "0fb3xbw9a0ah4viwp3a7hr5phnc7mvcl9ba2yjidncpqmspypacx"; sha256 = "1b461ic5s3hjk3y70ldik82ny08rdywn1zfqa8d2jyyvnh4dya77";
}) })
(pkgs.fetchurl { (pkgs.fetchurl {
@ -16,13 +16,13 @@
}) })
(pkgs.fetchurl { (pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-headers-git-9.0.0.6451.a3f6d363d-1-any.pkg.tar.zst"; url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-headers-git-9.0.0.6454.b4445ee52-1-any.pkg.tar.zst";
sha256 = "1ngnjb9vgk295wlwqandm0nhqqdfrp584kx3nfshxkhwmj5gpzxk"; sha256 = "0wyg5ad3fh2lwd7avxvpncipj5wxmp647l43wzr1l3rrkd820yy3";
}) })
(pkgs.fetchurl { (pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-crt-git-9.0.0.6451.a3f6d363d-1-any.pkg.tar.zst"; url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-crt-git-9.0.0.6454.b4445ee52-1-any.pkg.tar.zst";
sha256 = "1ccipidbsjncdhr48k50ia53dwn7v3ghdl8f1svgwvnh3mrx0bww"; sha256 = "0bnzwgf395fbwbsq8900prj409b081hi0dd76kak6d971xqyy2r4";
}) })
(pkgs.fetchurl { (pkgs.fetchurl {
@ -46,8 +46,8 @@
}) })
(pkgs.fetchurl { (pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-libwinpthread-git-9.0.0.6451.a3f6d363d-1-any.pkg.tar.zst"; url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-libwinpthread-git-9.0.0.6454.b4445ee52-1-any.pkg.tar.zst";
sha256 = "0qdy79l5y02lw2xa8i3j6yayhz8a7awfgyyd82pcmbzwx57q2xqb"; sha256 = "181fm72bi6cs348hixkqjzivizzcsyn2lxvnbqprx4366prjf7nn";
}) })
(pkgs.fetchurl { (pkgs.fetchurl {
@ -61,8 +61,8 @@
}) })
(pkgs.fetchurl { (pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-winpthreads-git-9.0.0.6451.a3f6d363d-1-any.pkg.tar.zst"; url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-winpthreads-git-9.0.0.6454.b4445ee52-1-any.pkg.tar.zst";
sha256 = "08zwgkrp45y5ry8avz61krasjkk4k4a5rrdz4nd78bbbah84mpgz"; sha256 = "0n3pim85wlsc93y9sh06rnfraqgzbz300sp9hd8n7wgvcsmpj9rx";
}) })
(pkgs.fetchurl { (pkgs.fetchurl {
@ -86,8 +86,8 @@
}) })
(pkgs.fetchurl { (pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-expat-2.4.7-1-any.pkg.tar.zst"; url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-expat-2.4.8-1-any.pkg.tar.zst";
sha256 = "19qh3kk2kmkkzxirpx1swgfsfb29gy9q8qgdmrzzbwrqrn8vs77j"; sha256 = "1qkw4k61ddaflns5ms0xh0czbx99wxhs0dfbk8sv8by2rkshl51k";
}) })
(pkgs.fetchurl { (pkgs.fetchurl {
@ -101,8 +101,8 @@
}) })
(pkgs.fetchurl { (pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-libidn2-2.3.1-1-any.pkg.tar.zst"; url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-libidn2-2.3.2-1-any.pkg.tar.zst";
sha256 = "00vm6d56ldr1f4h0dn15j0ja17dif45qxlxaqv4x5nw555frklf5"; sha256 = "0p694g8g716zyvcxc48xmdanjyrkp3ir4naqlradrw2szy9bj800";
}) })
(pkgs.fetchurl { (pkgs.fetchurl {
@ -231,8 +231,8 @@
}) })
(pkgs.fetchurl { (pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-cmake-3.22.3-2-any.pkg.tar.zst"; url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-cmake-3.23.0-1-any.pkg.tar.zst";
sha256 = "1xp8n5s98va7a9cq36d9p49lk32yv9vkvlayc4d4j465xzm21hgp"; sha256 = "1gcmm3bd29zfd88ack6xdqwmbskyvhb4ymrfspayk4jfcj4svky5";
}) })
(pkgs.fetchurl { (pkgs.fetchurl {
@ -266,8 +266,8 @@
}) })
(pkgs.fetchurl { (pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-sqlite3-3.38.1-1-any.pkg.tar.zst"; url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-sqlite3-3.38.2-1-any.pkg.tar.zst";
sha256 = "04h4m72mwmad82nzrl5qj9wlsinjs7z7bsbbq17dxkx92aj75p26"; sha256 = "14agi4611h5400j7bqh1w5i5qbhp4hp6apzaddilxq97y37vj90q";
}) })
(pkgs.fetchurl { (pkgs.fetchurl {
@ -291,12 +291,17 @@
}) })
(pkgs.fetchurl { (pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-openblas-0.3.20-1-any.pkg.tar.zst"; url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-openblas-0.3.20-2-any.pkg.tar.zst";
sha256 = "1adbbycbvs2nkjhgap92fk3x0vqfjb3ghhvyd1xlnn56c5n0iphf"; sha256 = "1cj0j06b2xhcvq9v6jx5cn130r8pkv2xa885f3z3z98kmrifjw0l";
}) })
(pkgs.fetchurl { (pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-python-numpy-1.21.5-1-any.pkg.tar.zst"; url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-python-numpy-1.21.5-1-any.pkg.tar.zst";
sha256 = "10bhfq65nrzxipgy75bqaad74daif4ay06phwvbx70b9j0wm33c3"; sha256 = "10bhfq65nrzxipgy75bqaad74daif4ay06phwvbx70b9j0wm33c3";
}) })
(pkgs.fetchurl {
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-python-setuptools-59.8.0-2-any.pkg.tar.zst";
sha256 = "1jbvsmh1r00yb6fm8by6z7xp9069f9lkj73jrsnxmflmvpdkjl5c";
})
] ]