forked from M-Labs/nac3
Compare commits
36 Commits
master
...
1f5d338c04
Author | SHA1 | Date |
---|---|---|
ychenfo | 1f5d338c04 | |
ychenfo | f4e9c2eb31 | |
ychenfo | 0059b389ae | |
pca006132 | 4f66bdeda9 | |
Sebastien Bourdeauducq | 57369896d7 | |
ychenfo | 2edeb31d21 | |
ychenfo | b8ef44d64e | |
ychenfo | c3156afebd | |
ychenfo | 388c9b7241 | |
ychenfo | e52d7fc97a | |
ychenfo | 6ab73a223c | |
ychenfo | a38cc04444 | |
ychenfo | 1f5826d352 | |
Sebastien Bourdeauducq | 94eebde4ea | |
Sebastien Bourdeauducq | 63ec382673 | |
Sebastien Bourdeauducq | 0ca1a7bedb | |
Sebastien Bourdeauducq | 201ca3f63d | |
Sebastien Bourdeauducq | 19182759cd | |
Sebastien Bourdeauducq | edd039abdc | |
Sebastien Bourdeauducq | 3852cc1058 | |
Sebastien Bourdeauducq | 0600ee8efa | |
ychenfo | bed33a7421 | |
ychenfo | 0d2b844a2e | |
ychenfo | 8d7e300a4a | |
ychenfo | 10d623e36f | |
ychenfo | 000b128551 | |
Sebastien Bourdeauducq | e4581a6d9b | |
pca006132 | 1a82d296e7 | |
pca006132 | bf067e2481 | |
ychenfo | ba8ed6c663 | |
ychenfo | 26a4834254 | |
Sebastien Bourdeauducq | 1ad4b0227c | |
Sebastien Bourdeauducq | 6288a66dc5 | |
Sebastien Bourdeauducq | de4320eefb | |
Sebastien Bourdeauducq | a380cd5010 | |
ychenfo | 80631fc92b |
|
@ -249,9 +249,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.5"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77"
|
||||
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
|
@ -278,9 +278,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "1.8.0"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "282a6247722caba404c065016bbfa522806e51714c34f5dfc3e4a3a46fcb4223"
|
||||
checksum = "0f647032dfaa1f8b6dc29bd3edb7bbef4861b8b8007ebb118d6db284fd59f6ee"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"hashbrown",
|
||||
|
@ -329,7 +329,7 @@ dependencies = [
|
|||
"libc",
|
||||
"llvm-sys",
|
||||
"once_cell",
|
||||
"parking_lot",
|
||||
"parking_lot 0.11.2",
|
||||
"regex",
|
||||
]
|
||||
|
||||
|
@ -457,10 +457,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.6"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b"
|
||||
checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
|
@ -495,7 +496,7 @@ dependencies = [
|
|||
"inkwell",
|
||||
"nac3core",
|
||||
"nac3parser",
|
||||
"parking_lot",
|
||||
"parking_lot 0.11.2",
|
||||
"pyo3",
|
||||
"tempfile",
|
||||
]
|
||||
|
@ -506,7 +507,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"fxhash",
|
||||
"lazy_static",
|
||||
"parking_lot",
|
||||
"parking_lot 0.11.2",
|
||||
"string-interner",
|
||||
]
|
||||
|
||||
|
@ -520,7 +521,7 @@ dependencies = [
|
|||
"insta",
|
||||
"itertools",
|
||||
"nac3parser",
|
||||
"parking_lot",
|
||||
"parking_lot 0.11.2",
|
||||
"rayon",
|
||||
"regex",
|
||||
"test-case",
|
||||
|
@ -549,7 +550,7 @@ dependencies = [
|
|||
"inkwell",
|
||||
"nac3core",
|
||||
"nac3parser",
|
||||
"parking_lot",
|
||||
"parking_lot 0.11.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -582,7 +583,17 @@ checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99"
|
|||
dependencies = [
|
||||
"instant",
|
||||
"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]]
|
||||
|
@ -599,6 +610,19 @@ dependencies = [
|
|||
"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]]
|
||||
name = "paste"
|
||||
version = "0.1.18"
|
||||
|
@ -732,7 +756,7 @@ dependencies = [
|
|||
"cfg-if",
|
||||
"indoc 0.3.6",
|
||||
"libc",
|
||||
"parking_lot",
|
||||
"parking_lot 0.11.2",
|
||||
"paste",
|
||||
"pyo3-build-config",
|
||||
"pyo3-macros",
|
||||
|
@ -773,9 +797,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.16"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4af2ec4714533fcdf07e886f17025ace8b997b9ce51204ee69b6da831c3da57"
|
||||
checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
@ -837,18 +861,18 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.11"
|
||||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8380fe0152551244f0747b1bf41737e0f8a74f97a14ccefd1148187271634f3c"
|
||||
checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.2"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7776223e2696f1aa4c6b0170e83212f47296a00424305117d013dfe86fb0fe55"
|
||||
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"redox_syscall",
|
||||
|
@ -998,22 +1022,22 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "string_cache"
|
||||
version = "0.8.3"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "33994d0838dc2d152d17a62adf608a869b5e846b65b389af7f3dbc1de45c5b26"
|
||||
checksum = "213494b7a2b503146286049378ce02b482200519accc31872ee8be91fa820a08"
|
||||
dependencies = [
|
||||
"lazy_static",
|
||||
"new_debug_unreachable",
|
||||
"parking_lot",
|
||||
"once_cell",
|
||||
"parking_lot 0.12.0",
|
||||
"phf_shared 0.10.0",
|
||||
"precomputed-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.89"
|
||||
version = "1.0.90"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea297be220d52398dcc07ce15a209fce436d361735ac1db700cab3b6cdfb9f54"
|
||||
checksum = "704df27628939572cd88d33f171cd6f896f4eaca85252c6e0a72d8d8287ee86f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -1207,6 +1231,49 @@ version = "0.4.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "yaml-rust"
|
||||
version = "0.4.5"
|
||||
|
|
|
@ -40,17 +40,20 @@ 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:
|
||||
```
|
||||
pacman -S unzip
|
||||
wget https://nixbld.m-labs.hk/build/97899/download/1/nac3artiq.zip # edit the build number
|
||||
wget https://nixbld.m-labs.hk/build/115529/download/1/nac3artiq.zip # edit the build number
|
||||
unzip nac3artiq.zip -d C:/msys64/mingw64/lib/python3.9/site-packages
|
||||
```
|
||||
|
||||
Install additional NAC3 dependencies:
|
||||
Do the same for ``lld-msys2``:
|
||||
```
|
||||
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.
|
||||
|
||||
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
|
||||
|
||||
This repository contains:
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1647992509,
|
||||
"narHash": "sha256-AG40Nt5OWz0LBs5p457emOuwLKOvTtcv/2fUdnEN3Ws=",
|
||||
"lastModified": 1648553562,
|
||||
"narHash": "sha256-xQhRKu6h0phd56oCzGjkhHkY4eDI1XKedGqkFtlXapk=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "d2caa9377539e3b5ff1272ac3aa2d15f3081069f",
|
||||
"rev": "9b168e5e62406fa2e55e132f390379a6ba22b402",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
11
flake.nix
11
flake.nix
|
@ -8,7 +8,7 @@
|
|||
pkgs = import nixpkgs { system = "x86_64-linux"; };
|
||||
in rec {
|
||||
packages.x86_64-linux = rec {
|
||||
llvm-nac3 = pkgs.callPackage "${self}/llvm" {};
|
||||
llvm-nac3 = pkgs.callPackage ./nix/llvm {};
|
||||
nac3artiq = pkgs.python3Packages.toPythonModule (
|
||||
pkgs.rustPlatform.buildRustPackage {
|
||||
name = "nac3artiq";
|
||||
|
@ -48,7 +48,7 @@
|
|||
};
|
||||
|
||||
# LLVM PGO support
|
||||
llvm-nac3-instrumented = pkgs.callPackage "${self}/llvm" {
|
||||
llvm-nac3-instrumented = pkgs.callPackage ./nix/llvm {
|
||||
stdenv = pkgs.llvmPackages_13.stdenv;
|
||||
extraCmakeFlags = [ "-DLLVM_BUILD_INSTRUMENTED=IR" ];
|
||||
};
|
||||
|
@ -86,7 +86,7 @@
|
|||
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;
|
||||
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 {
|
||||
name = "nac3-dev-shell";
|
||||
|
@ -129,7 +129,7 @@
|
|||
];
|
||||
};
|
||||
devShells.x86_64-linux.msys2 = pkgs.mkShell {
|
||||
name = "nac3-dev-shell";
|
||||
name = "nac3-dev-shell-msys2";
|
||||
buildInputs = with pkgs; [
|
||||
curl
|
||||
pacman
|
||||
|
@ -142,6 +142,7 @@
|
|||
inherit (packages.x86_64-linux) llvm-nac3 nac3artiq;
|
||||
llvm-nac3-msys2 = packages.x86_64-w64-mingw32.llvm-nac3;
|
||||
nac3artiq-msys2 = packages.x86_64-w64-mingw32.nac3artiq;
|
||||
lld-msys2 = packages.x86_64-w64-mingw32.lld;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ class EmbeddingMap:
|
|||
self.string_map = {}
|
||||
self.string_reverse_map = {}
|
||||
self.function_map = {}
|
||||
self.attributes_writeback = []
|
||||
|
||||
# preallocate exception names
|
||||
self.preallocate_runtime_exception_names(["RuntimeError",
|
||||
|
@ -16,7 +17,8 @@ class EmbeddingMap:
|
|||
"CacheError",
|
||||
"SPIError",
|
||||
"0:ZeroDivisionError",
|
||||
"0:IndexError"])
|
||||
"0:IndexError",
|
||||
"0:UnwrapNoneError"])
|
||||
|
||||
def preallocate_runtime_exception_names(self, names):
|
||||
for i, name in enumerate(names):
|
||||
|
|
|
@ -11,6 +11,7 @@ from embedding_map import EmbeddingMap
|
|||
|
||||
__all__ = [
|
||||
"Kernel", "KernelInvariant", "virtual",
|
||||
"Option", "Some", "none", "UnwrapNoneError",
|
||||
"round64", "floor64", "ceil64",
|
||||
"extern", "kernel", "portable", "nac3",
|
||||
"rpc", "ms", "us", "ns",
|
||||
|
@ -32,6 +33,39 @@ class KernelInvariant(Generic[T]):
|
|||
class virtual(Generic[T]):
|
||||
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):
|
||||
return round(x)
|
||||
|
@ -240,5 +274,10 @@ class KernelContextManager:
|
|||
def __exit__(self):
|
||||
pass
|
||||
|
||||
@nac3
|
||||
class UnwrapNoneError(Exception):
|
||||
"""raised when unwrapping a none value"""
|
||||
artiq_builtin = True
|
||||
|
||||
parallel = KernelContextManager()
|
||||
sequential = KernelContextManager()
|
||||
|
|
|
@ -6,7 +6,7 @@ use nac3core::{
|
|||
},
|
||||
symbol_resolver::ValueEnum,
|
||||
toplevel::{DefinitionId, GenCall},
|
||||
typecheck::typedef::{FunSignature, Type},
|
||||
typecheck::typedef::{FunSignature, FuncArg, Type, TypeEnum}
|
||||
};
|
||||
|
||||
use nac3parser::ast::{Expr, ExprKind, Located, Stmt, StmtKind, StrRef};
|
||||
|
@ -15,7 +15,9 @@ use inkwell::{
|
|||
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::{
|
||||
collections::hash_map::DefaultHasher,
|
||||
|
@ -270,8 +272,6 @@ fn gen_rpc_tag<'ctx, 'a>(
|
|||
buffer.push(b'l');
|
||||
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))),
|
||||
}
|
||||
}
|
||||
|
@ -291,7 +291,7 @@ fn rpc_codegen_callback_fn<'ctx, 'a>(
|
|||
let int32 = ctx.ctx.i32_type();
|
||||
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
|
||||
let mut tag = Vec::new();
|
||||
if obj.is_some() {
|
||||
|
@ -361,7 +361,10 @@ fn rpc_codegen_callback_fn<'ctx, 'a>(
|
|||
}
|
||||
// default value handling
|
||||
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
|
||||
let mut real_params = fun
|
||||
|
@ -486,6 +489,81 @@ fn rpc_codegen_callback_fn<'ctx, 'a>(
|
|||
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> {
|
||||
Arc::new(GenCall::new(Box::new(|ctx, obj, fun, args, generator| {
|
||||
rpc_codegen_callback_fn(ctx, obj, fun, args, generator)
|
||||
|
|
|
@ -10,6 +10,7 @@ use inkwell::{
|
|||
targets::*,
|
||||
OptimizationLevel,
|
||||
};
|
||||
use nac3core::codegen::gen_func_impl;
|
||||
use nac3core::toplevel::builtins::get_exn_constructor;
|
||||
use nac3core::typecheck::typedef::{TypeEnum, Unifier};
|
||||
use nac3parser::{
|
||||
|
@ -36,6 +37,7 @@ use nac3core::{
|
|||
|
||||
use tempfile::{self, TempDir};
|
||||
|
||||
use crate::codegen::attributes_writeback;
|
||||
use crate::{
|
||||
codegen::{rpc_codegen_callback, ArtiqCodeGenerator},
|
||||
symbol_resolver::{InnerResolver, PythonHelper, Resolver, DeferredEvaluationStore},
|
||||
|
@ -71,6 +73,7 @@ pub struct PrimitivePythonId {
|
|||
exception: u64,
|
||||
generic_alias: (u64, u64),
|
||||
virtual_id: u64,
|
||||
option: u64,
|
||||
}
|
||||
|
||||
type TopLevelComponent = (Stmt, String, PyObject);
|
||||
|
@ -373,7 +376,17 @@ impl Nac3 {
|
|||
get_attr_id(typing_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"),
|
||||
int: get_attr_id(builtins_mod, "int"),
|
||||
int32: get_attr_id(numpy_mod, "int32"),
|
||||
|
@ -385,6 +398,17 @@ impl Nac3 {
|
|||
list: get_attr_id(builtins_mod, "list"),
|
||||
tuple: get_attr_id(builtins_mod, "tuple"),
|
||||
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();
|
||||
|
@ -454,6 +478,8 @@ impl Nac3 {
|
|||
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_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 {
|
||||
id_fn: builtins.getattr("id").unwrap().to_object(py),
|
||||
len_fn: builtins.getattr("len").unwrap().to_object(py),
|
||||
|
@ -474,13 +500,13 @@ impl Nac3 {
|
|||
"KeyError",
|
||||
"NotImplementedError",
|
||||
"OverflowError",
|
||||
"IOError"
|
||||
"IOError",
|
||||
"UnwrapNoneError",
|
||||
];
|
||||
add_exceptions(&mut composer, &mut builtins_def, &mut builtins_ty, &exception_names);
|
||||
|
||||
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![];
|
||||
for (stmt, path, module) in self.top_levels.iter() {
|
||||
let py_module: &PyAny = module.extract(py)?;
|
||||
|
@ -594,7 +620,7 @@ impl Nac3 {
|
|||
};
|
||||
let mut synthesized =
|
||||
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_def: builtins_def.clone().into(),
|
||||
pyid_to_def: self.pyid_to_def.clone(),
|
||||
|
@ -611,17 +637,18 @@ impl Nac3 {
|
|||
string_store: self.string_store.clone(),
|
||||
exception_ids: self.exception_ids.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
|
||||
.register_top_level(synthesized.pop().unwrap(), Some(resolver.clone()), "".into())
|
||||
.unwrap();
|
||||
|
||||
let signature =
|
||||
let fun_signature =
|
||||
FunSignature { args: vec![], ret: self.primitive.none, vars: HashMap::new() };
|
||||
let mut store = ConcreteTypeStore::new();
|
||||
let mut cache = HashMap::new();
|
||||
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);
|
||||
|
||||
if let Err(e) = composer.start_analysis(true) {
|
||||
|
@ -698,12 +725,29 @@ impl Nac3 {
|
|||
symbol_name: "__modinit__".to_string(),
|
||||
body: instance.body,
|
||||
signature,
|
||||
resolver,
|
||||
resolver: resolver.clone(),
|
||||
store,
|
||||
unifier_index: instance.unifier_id,
|
||||
calls: instance.calls,
|
||||
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 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)))
|
||||
.collect();
|
||||
|
||||
let membuffer = membuffers.clone();
|
||||
py.allow_threads(|| {
|
||||
let (registry, handles) = WorkerRegistry::create_workers(threads, top_level.clone(), f);
|
||||
registry.add_task(task);
|
||||
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, ®istry, 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 buffers = membuffers.lock();
|
||||
let main = context
|
||||
.create_module_from_ir(MemoryBuffer::create_from_memory_range(&buffers[0], "main"))
|
||||
.unwrap();
|
||||
|
@ -742,6 +799,11 @@ impl Nac3 {
|
|||
main.link_in_module(other)
|
||||
.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))
|
||||
.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() {
|
||||
return Err(CompileError::new_err("failed to start linker"));
|
||||
}
|
||||
|
|
|
@ -15,7 +15,7 @@ use pyo3::{
|
|||
PyAny, PyObject, PyResult, Python,
|
||||
};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
collections::HashMap,
|
||||
sync::{
|
||||
Arc,
|
||||
atomic::{AtomicBool, Ordering::Relaxed}
|
||||
|
@ -54,7 +54,7 @@ pub struct InnerResolver {
|
|||
pub id_to_pyval: RwLock<HashMap<StrRef, (u64, PyObject)>>,
|
||||
pub id_to_primitive: RwLock<HashMap<u64, PrimitiveValue>>,
|
||||
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 pyid_to_def: Arc<RwLock<HashMap<u64, DefinitionId>>>,
|
||||
pub pyid_to_type: Arc<RwLock<HashMap<u64, Type>>>,
|
||||
|
@ -279,6 +279,10 @@ impl InnerResolver {
|
|||
} else if ty_id == self.primitive_ids.tuple {
|
||||
// do not handle type var param and concrete check here
|
||||
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() {
|
||||
let def = defs[def_id.0].read();
|
||||
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,
|
||||
py: Python,
|
||||
obj: &PyAny,
|
||||
|
@ -508,8 +512,8 @@ impl InnerResolver {
|
|||
primitives: &PrimitiveStore,
|
||||
) -> PyResult<Result<Type, String>> {
|
||||
let ty = self.helper.type_fn.call1(py, (obj,)).unwrap();
|
||||
let ty_id: u64 = self.helper.id_fn.call1(py, (ty.clone(),))?.extract(py)?;
|
||||
if let Some(ty) = self.pyid_to_type.read().get(&ty_id) {
|
||||
let py_obj_id: u64 = self.helper.id_fn.call1(py, (obj,))?.extract(py)?;
|
||||
if let Some(ty) = self.pyid_to_type.read().get(&py_obj_id) {
|
||||
return Ok(Ok(*ty))
|
||||
}
|
||||
let (extracted_ty, inst_check) = match self.get_pyty_obj_type(
|
||||
|
@ -569,7 +573,54 @@ impl InnerResolver {
|
|||
let types = 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) => {
|
||||
self.pyid_to_type.write().insert(py_obj_id, extracted_ty);
|
||||
let var_map = params
|
||||
.iter()
|
||||
.map(|(id_var, ty)| {
|
||||
|
@ -623,13 +674,19 @@ impl InnerResolver {
|
|||
let extracted_ty = unifier.subst(extracted_ty, &var_map).unwrap_or(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)),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_obj_value<'ctx, 'a>(
|
||||
pub fn get_obj_value<'ctx, 'a>(
|
||||
&self,
|
||||
py: Python,
|
||||
obj: &PyAny,
|
||||
|
@ -697,13 +754,13 @@ impl InnerResolver {
|
|||
.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(|| {
|
||||
ctx.module.add_global(arr_ty, Some(AddressSpace::Generic), &id_str)
|
||||
});
|
||||
return Ok(Some(global.as_pointer_value().into()));
|
||||
} 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()))
|
||||
} 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 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<_>>, _> =
|
||||
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();
|
||||
let val = val?.unwrap();
|
||||
let val = ctx.ctx.const_struct(&val, false);
|
||||
let global = ctx.module.add_global(ty, Some(AddressSpace::Generic), &id_str);
|
||||
global.set_initializer(&val);
|
||||
Ok(Some(global.as_pointer_value().into()))
|
||||
Ok(Some(val.into()))
|
||||
} else if ty_id == self.primitive_ids.option {
|
||||
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 {
|
||||
let id_str = id.to_string();
|
||||
|
||||
|
@ -812,13 +865,13 @@ impl InnerResolver {
|
|||
.get_element_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(|| {
|
||||
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);
|
||||
self.global_value_ids.write().insert(id, obj.into());
|
||||
}
|
||||
}
|
||||
// should be classes
|
||||
|
@ -827,15 +880,32 @@ impl InnerResolver {
|
|||
if let TopLevelDef::Class { fields, .. } = &*definition {
|
||||
let values: Result<Option<Vec<_>>, _> = fields
|
||||
.iter()
|
||||
.map(|(name, _, _)| {
|
||||
self.get_obj_value(py, obj.getattr(&name.to_string())?, ctx, generator).map_err(|e|
|
||||
super::CompileError::new_err(format!("Error getting field {}: {}", name, e)))
|
||||
.map(|(name, ty, _)| {
|
||||
let v = self.get_obj_value(py, obj.getattr(&name.to_string())?, ctx, generator)
|
||||
.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();
|
||||
let values = values?;
|
||||
if let Some(values) = 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);
|
||||
Ok(Some(global.as_pointer_value().into()))
|
||||
} else {
|
||||
|
@ -852,6 +922,7 @@ impl InnerResolver {
|
|||
py: Python,
|
||||
obj: &PyAny,
|
||||
) -> PyResult<Result<SymbolValue, String>> {
|
||||
let id: u64 = self.helper.id_fn.call1(py, (obj,))?.extract(py)?;
|
||||
let ty_id: u64 =
|
||||
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 {
|
||||
|
@ -860,6 +931,12 @@ impl InnerResolver {
|
|||
} else if ty_id == self.primitive_ids.int64 {
|
||||
let val: i64 = obj.extract()?;
|
||||
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 {
|
||||
let val: bool = obj.extract()?;
|
||||
Ok(SymbolValue::Bool(val))
|
||||
|
@ -870,13 +947,17 @@ impl InnerResolver {
|
|||
let elements: &PyTuple = obj.cast_as()?;
|
||||
let elements: Result<Result<Vec<_>, String>, _> =
|
||||
elements.iter().map(|elem| self.get_default_param_obj_value(py, elem)).collect();
|
||||
let elements = match elements? {
|
||||
Ok(el) => el,
|
||||
Err(err) => return Ok(Err(err)),
|
||||
};
|
||||
Ok(SymbolValue::Tuple(elements))
|
||||
elements?.map(SymbolValue::Tuple)
|
||||
} else if ty_id == self.primitive_ids.option {
|
||||
if id == self.primitive_ids.none {
|
||||
Ok(SymbolValue::OptionNone)
|
||||
} else {
|
||||
self
|
||||
.get_default_param_obj_value(py, obj.getattr("_nac3_option").unwrap())?
|
||||
.map(|v| SymbolValue::OptionSome(Box::new(v)))
|
||||
}
|
||||
} 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() {
|
||||
let key: &str = key.extract()?;
|
||||
if key == id.to_string() {
|
||||
sym_value =
|
||||
Some(self.0.get_default_param_obj_value(py, val).unwrap().unwrap());
|
||||
if let Ok(Ok(v)) = self.0.get_default_param_obj_value(py, val) {
|
||||
sym_value = Some(v)
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -901,7 +983,7 @@ impl SymbolResolver for Resolver {
|
|||
})
|
||||
.unwrap()
|
||||
}
|
||||
_ => unimplemented!("other type of expr not supported at {}", expr.location),
|
||||
_ => unreachable!("only for resolving names"),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -91,6 +91,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
&mut self,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
val: &SymbolValue,
|
||||
ty: Type,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
match val {
|
||||
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()
|
||||
}
|
||||
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 ty = self.ctx.struct_type(&fields, false);
|
||||
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")
|
||||
}
|
||||
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,
|
||||
self.top_level,
|
||||
&mut self.type_cache,
|
||||
&self.primitives,
|
||||
ty,
|
||||
)
|
||||
}
|
||||
|
@ -354,9 +387,9 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn gen_string<G: CodeGenerator, S: Into<String>>(
|
||||
pub fn gen_string<S: Into<String>>(
|
||||
&mut self,
|
||||
generator: &mut G,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
s: S,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
self.gen_const(generator, &nac3parser::ast::Constant::Str(s.into()), self.primitives.str)
|
||||
|
@ -409,6 +442,19 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
err_msg: &str,
|
||||
params: [Option<IntValue<'ctx>>; 3],
|
||||
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<G: CodeGenerator>(
|
||||
&mut self,
|
||||
generator: &mut G,
|
||||
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_true = i1.const_all_ones();
|
||||
|
@ -435,7 +481,6 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
let exn_block = self.ctx.append_basic_block(current_fun, "fail");
|
||||
self.builder.build_conditional_branch(cond, then_block, 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.builder.position_at_end(then_block);
|
||||
}
|
||||
|
@ -592,7 +637,7 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
|
|||
}
|
||||
mapping.insert(
|
||||
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
|
||||
|
@ -903,15 +948,12 @@ pub fn gen_binop_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
} else if ty1 == ctx.primitives.float && ty2 == ctx.primitives.int32 {
|
||||
// Pow is the only operator that would pass typecheck between float and int
|
||||
assert!(*op == Operator::Pow);
|
||||
// TODO: throw exception when rhs is out of i16 bound
|
||||
// since llvm intrinsic only support to i16 for f64
|
||||
let i16_t = ctx.ctx.i16_type();
|
||||
let pow_intr = ctx.module.get_function("llvm.powi.f64.i16").unwrap_or_else(|| {
|
||||
let i32_t = ctx.ctx.i32_type();
|
||||
let pow_intr = ctx.module.get_function("llvm.powi.f64.i32").unwrap_or_else(|| {
|
||||
let f64_t = ctx.ctx.f64_type();
|
||||
let ty = f64_t.fn_type(&[f64_t.into(), i16_t.into()], false);
|
||||
ctx.module.add_function("llvm.powi.f64.i16", ty, None)
|
||||
let ty = f64_t.fn_type(&[f64_t.into(), i32_t.into()], false);
|
||||
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
|
||||
.build_call(pow_intr, &[left.into(), right.into()], "f_pow_i")
|
||||
.try_as_basic_value()
|
||||
|
@ -934,23 +976,45 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
let ty = expr.custom.unwrap();
|
||||
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) {
|
||||
Some((ptr, None, _)) => ctx.builder.build_load(*ptr, "load").into(),
|
||||
Some((_, Some(static_value), _)) => ValueEnum::Static(static_value.clone()),
|
||||
None => {
|
||||
let resolver = ctx.resolver.clone();
|
||||
let val = resolver.get_symbol_value(*id, ctx).unwrap();
|
||||
// if is tuple, need to deref it to handle tuple as value
|
||||
if let (TypeEnum::TTuple { .. }, BasicValueEnum::PointerValue(ptr)) = (
|
||||
&*ctx.unifier.get_ty(expr.custom.unwrap()),
|
||||
resolver
|
||||
.get_symbol_value(*id, ctx)
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)?,
|
||||
) {
|
||||
ctx.builder.build_load(ptr, "tup_val").into()
|
||||
} else {
|
||||
val
|
||||
// if is option, need to cast pointer to handle None
|
||||
match &*ctx.unifier.get_ty(expr.custom.unwrap()) {
|
||||
TypeEnum::TObj { obj_id, params, .. }
|
||||
if *obj_id == ctx.primitives.option.get_obj_id(&ctx.unifier) =>
|
||||
{
|
||||
if let BasicValueEnum::PointerValue(ptr) = val.to_basic_value_enum(ctx, generator)? {
|
||||
let actual_ptr_ty = ctx.get_llvm_type(
|
||||
generator,
|
||||
*params.iter().next().unwrap().1,
|
||||
)
|
||||
.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 +1026,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
.map(|x| {
|
||||
generator
|
||||
.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<_>, _>>()?;
|
||||
let ty = if elements.is_empty() {
|
||||
|
@ -995,7 +1059,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
.map(|x| {
|
||||
generator
|
||||
.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<_>, _>>()?;
|
||||
let element_ty = element_val.iter().map(BasicValueEnum::get_type).collect_vec();
|
||||
|
@ -1023,7 +1087,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
v.into_pointer_value(),
|
||||
&[zero, int32.const_int(index as u64, false)],
|
||||
))) as Result<_, String>
|
||||
}, |v| Ok(v))?,
|
||||
}, Ok)?,
|
||||
ValueEnum::Dynamic(v) => {
|
||||
let index = ctx.get_attr_index(value.custom.unwrap(), *attr);
|
||||
ValueEnum::Dynamic(ctx.build_gep_and_load(
|
||||
|
@ -1204,21 +1268,44 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)?
|
||||
.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 then_bb = ctx.ctx.append_basic_block(current, "then");
|
||||
let else_bb = ctx.ctx.append_basic_block(current, "else");
|
||||
let cont_bb = ctx.ctx.append_basic_block(current, "cont");
|
||||
ctx.builder.build_conditional_branch(test, then_bb, else_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.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.position_at_end(cont_bb);
|
||||
let phi = ctx.builder.build_phi(a.get_type(), "ifexpr");
|
||||
phi.add_incoming(&[(&a, then_bb), (&b, else_bb)]);
|
||||
phi.as_basic_value().into()
|
||||
match result {
|
||||
None => return Ok(None),
|
||||
Some(v) => return Ok(Some(ctx.builder.build_load(v, "if_exp_val_load").into()))
|
||||
}
|
||||
}
|
||||
ExprKind::Call { func, args, keywords } => {
|
||||
let mut params = args
|
||||
|
@ -1281,6 +1368,26 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
unreachable!()
|
||||
}
|
||||
};
|
||||
// directly generate code for option.unwrap
|
||||
// since it needs location information from ast
|
||||
if attr == &"unwrap".into()
|
||||
&& id == ctx.primitives.option.get_obj_id(&ctx.unifier)
|
||||
{
|
||||
if let BasicValueEnum::PointerValue(ptr) = val.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],
|
||||
expr.location,
|
||||
);
|
||||
return Ok(Some(ctx.builder.build_load(ptr, "unwrap_some").into()))
|
||||
} else {
|
||||
unreachable!("option must be ptr")
|
||||
}
|
||||
}
|
||||
return Ok(generator
|
||||
.gen_call(
|
||||
ctx,
|
||||
|
|
|
@ -259,6 +259,7 @@ fn get_llvm_type<'ctx>(
|
|||
unifier: &mut Unifier,
|
||||
top_level: &TopLevelContext,
|
||||
type_cache: &mut HashMap<Type, BasicTypeEnum<'ctx>>,
|
||||
primitives: &PrimitiveStore,
|
||||
ty: Type,
|
||||
) -> BasicTypeEnum<'ctx> {
|
||||
use TypeEnum::*;
|
||||
|
@ -268,9 +269,28 @@ fn get_llvm_type<'ctx>(
|
|||
let ty_enum = unifier.get_ty(ty);
|
||||
let result = match &*ty_enum {
|
||||
TObj { obj_id, fields, .. } => {
|
||||
// check to avoid treating primitives as classes
|
||||
if obj_id.0 <= 7 {
|
||||
unreachable!();
|
||||
// check to avoid treating primitives other than Option as classes
|
||||
if obj_id.0 <= 10 {
|
||||
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
|
||||
let top_level_defs = top_level.definitions.read();
|
||||
|
@ -289,6 +309,7 @@ fn get_llvm_type<'ctx>(
|
|||
unifier,
|
||||
top_level,
|
||||
type_cache,
|
||||
primitives,
|
||||
fields[&f.0].0,
|
||||
)
|
||||
})
|
||||
|
@ -304,14 +325,14 @@ fn get_llvm_type<'ctx>(
|
|||
// a struct with fields in the order present in the tuple
|
||||
let fields = ty
|
||||
.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();
|
||||
ctx.struct_type(&fields, false).into()
|
||||
}
|
||||
TList { ty } => {
|
||||
// a struct with an integer and a pointer to an array
|
||||
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 = [
|
||||
element_type.ptr_type(AddressSpace::Generic).into(),
|
||||
generator.get_size_type(ctx).into(),
|
||||
|
@ -339,13 +360,14 @@ fn need_sret<'ctx>(ctx: &'ctx Context, ty: BasicTypeEnum<'ctx>) -> bool {
|
|||
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,
|
||||
generator: &mut G,
|
||||
registry: &WorkerRegistry,
|
||||
builder: Builder<'ctx>,
|
||||
module: Module<'ctx>,
|
||||
task: CodeGenTask,
|
||||
codegen_function: F
|
||||
) -> Result<(Builder<'ctx>, Module<'ctx>, FunctionValue<'ctx>), (Builder<'ctx>, String)> {
|
||||
let top_level_ctx = registry.top_level_ctx.clone();
|
||||
let static_value_store = registry.static_value_store.clone();
|
||||
|
@ -385,6 +407,7 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
|
|||
range: unifier.get_representative(primitives.range),
|
||||
str: unifier.get_representative(primitives.str),
|
||||
exception: unifier.get_representative(primitives.exception),
|
||||
option: unifier.get_representative(primitives.option),
|
||||
};
|
||||
|
||||
let mut type_cache: HashMap<_, _> = [
|
||||
|
@ -417,6 +440,8 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
|
|||
exception.set_body(&fields, false);
|
||||
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, .. } =
|
||||
task.store.get(task.signature)
|
||||
|
@ -437,7 +462,7 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
|
|||
let ret_type = if unifier.unioned(ret, primitives.none) {
|
||||
None
|
||||
} 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));
|
||||
|
@ -450,6 +475,7 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
|
|||
&mut unifier,
|
||||
top_level_ctx.as_ref(),
|
||||
&mut type_cache,
|
||||
&primitives,
|
||||
arg.ty,
|
||||
)
|
||||
.into()
|
||||
|
@ -497,6 +523,7 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
|
|||
&mut unifier,
|
||||
top_level_ctx.as_ref(),
|
||||
&mut type_cache,
|
||||
&primitives,
|
||||
arg.ty,
|
||||
),
|
||||
&arg.name.to_string(),
|
||||
|
@ -546,25 +573,34 @@ pub fn gen_func<'ctx, G: CodeGenerator>(
|
|||
need_sret: has_sret
|
||||
};
|
||||
|
||||
let mut err = None;
|
||||
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;
|
||||
}
|
||||
}
|
||||
let result = codegen_function(generator, &mut code_gen_context);
|
||||
|
||||
// after static analysis, only void functions can have no return at the end.
|
||||
if !code_gen_context.is_terminated() {
|
||||
code_gen_context.builder.build_return(None);
|
||||
}
|
||||
|
||||
let CodeGenContext { builder, module, .. } = code_gen_context;
|
||||
if let Some(e) = err {
|
||||
if let Err(e) = result {
|
||||
return Err((builder, e));
|
||||
}
|
||||
|
||||
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(())
|
||||
})
|
||||
}
|
||||
|
|
|
@ -189,7 +189,8 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
|
|||
if ctx.unifier.unioned(iter.custom.unwrap(), ctx.primitives.range) {
|
||||
// setup
|
||||
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);
|
||||
ctx.builder.build_store(i, ctx.builder.build_int_sub(start, step, "start_init"));
|
||||
ctx.builder.build_unconditional_branch(test_bb);
|
||||
|
@ -207,6 +208,7 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
|
|||
"start_loop",
|
||||
);
|
||||
ctx.builder.build_store(i, tmp);
|
||||
ctx.builder.build_store(user_i, tmp);
|
||||
// // if step > 0, continue when i < end
|
||||
let cmp1 = ctx.builder.build_int_compare(inkwell::IntPredicate::SLT, tmp, end, "cmp1");
|
||||
// if step < 0, continue when i > end
|
||||
|
@ -973,7 +975,25 @@ pub fn gen_stmt<'ctx, 'a, G: CodeGenerator>(
|
|||
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(())
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ impl SymbolResolver for Resolver {
|
|||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_exception_id(&self, tyid: usize) -> usize {
|
||||
fn get_exception_id(&self, _tyid: usize) -> usize {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
@ -180,13 +180,22 @@ fn test_primitives() {
|
|||
let expected = indoc! {"
|
||||
; ModuleID = 'test'
|
||||
source_filename = \"test\"
|
||||
|
||||
|
||||
define i32 @testing(i32 %0, i32 %1) {
|
||||
init:
|
||||
%add = add i32 %0, %1
|
||||
%cmp = icmp eq i32 %add, 1
|
||||
%ifexpr = select i1 %cmp, i32 %0, i32 0
|
||||
ret i32 %ifexpr
|
||||
br i1 %cmp, label %then, label %else
|
||||
|
||||
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();
|
||||
|
|
|
@ -14,7 +14,7 @@ use crate::{
|
|||
typedef::{Type, Unifier},
|
||||
},
|
||||
};
|
||||
use inkwell::values::{BasicValueEnum, FloatValue, IntValue, PointerValue};
|
||||
use inkwell::values::{BasicValueEnum, FloatValue, IntValue, PointerValue, StructValue};
|
||||
use itertools::{chain, izip};
|
||||
use nac3parser::ast::{Expr, Location, StrRef};
|
||||
use parking_lot::RwLock;
|
||||
|
@ -29,6 +29,8 @@ pub enum SymbolValue {
|
|||
Double(f64),
|
||||
Bool(bool),
|
||||
Tuple(Vec<SymbolValue>),
|
||||
OptionSome(Box<SymbolValue>),
|
||||
OptionNone,
|
||||
}
|
||||
|
||||
impl Display for SymbolValue {
|
||||
|
@ -50,6 +52,8 @@ impl Display for SymbolValue {
|
|||
SymbolValue::Tuple(t) => {
|
||||
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> {
|
||||
pub fn to_basic_value_enum<'a>(
|
||||
self,
|
||||
|
@ -153,12 +163,11 @@ pub trait SymbolResolver {
|
|||
}
|
||||
|
||||
thread_local! {
|
||||
static IDENTIFIER_ID: [StrRef; 12] = [
|
||||
static IDENTIFIER_ID: [StrRef; 11] = [
|
||||
"int32".into(),
|
||||
"int64".into(),
|
||||
"float".into(),
|
||||
"bool".into(),
|
||||
"None".into(),
|
||||
"virtual".into(),
|
||||
"list".into(),
|
||||
"tuple".into(),
|
||||
|
@ -183,14 +192,13 @@ pub fn parse_type_annotation<T>(
|
|||
let int64_id = ids[1];
|
||||
let float_id = ids[2];
|
||||
let bool_id = ids[3];
|
||||
let none_id = ids[4];
|
||||
let virtual_id = ids[5];
|
||||
let list_id = ids[6];
|
||||
let tuple_id = ids[7];
|
||||
let str_id = ids[8];
|
||||
let exn_id = ids[9];
|
||||
let uint32_id = ids[10];
|
||||
let uint64_id = ids[11];
|
||||
let virtual_id = ids[4];
|
||||
let list_id = ids[5];
|
||||
let tuple_id = ids[6];
|
||||
let str_id = ids[7];
|
||||
let exn_id = ids[8];
|
||||
let uint32_id = ids[9];
|
||||
let uint64_id = ids[10];
|
||||
|
||||
let name_handling = |id: &StrRef, loc: Location, unifier: &mut Unifier| {
|
||||
if *id == int32_id {
|
||||
|
@ -205,8 +213,6 @@ pub fn parse_type_annotation<T>(
|
|||
Ok(primitives.float)
|
||||
} else if *id == bool_id {
|
||||
Ok(primitives.bool)
|
||||
} else if *id == none_id {
|
||||
Ok(primitives.none)
|
||||
} else if *id == str_id {
|
||||
Ok(primitives.str)
|
||||
} else if *id == exn_id {
|
||||
|
|
|
@ -105,6 +105,20 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
("__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![
|
||||
Arc::new(RwLock::new(TopLevelComposer::make_top_level_class_def(
|
||||
0,
|
||||
|
@ -180,6 +194,81 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
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(
|
||||
|_, _, _, _, _| {
|
||||
unreachable!("handled in gen_expr")
|
||||
},
|
||||
)))),
|
||||
loc: None,
|
||||
})),
|
||||
Arc::new(RwLock::new(TopLevelDef::Function {
|
||||
name: "int32".into(),
|
||||
simple_name: "int32".into(),
|
||||
|
@ -1098,6 +1187,28 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
)))),
|
||||
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<()>>> =
|
||||
|
@ -1123,6 +1234,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
"min",
|
||||
"max",
|
||||
"abs",
|
||||
"Some",
|
||||
],
|
||||
)
|
||||
}
|
||||
|
|
|
@ -74,6 +74,8 @@ impl TopLevelComposer {
|
|||
"self".into(),
|
||||
"Kernel".into(),
|
||||
"KernelInvariant".into(),
|
||||
"Some".into(),
|
||||
"Option".into(),
|
||||
]);
|
||||
let defined_names: HashSet<String> = Default::default();
|
||||
let method_class: HashMap<DefinitionId, DefinitionId> = Default::default();
|
||||
|
@ -89,10 +91,9 @@ impl TopLevelComposer {
|
|||
assert!(name == *simple_name);
|
||||
builtin_ty.insert(name, *signature);
|
||||
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!(type_vars.is_empty());
|
||||
if let Some(constructor) = constructor {
|
||||
builtin_ty.insert(*name, *constructor);
|
||||
}
|
||||
|
@ -1783,9 +1784,7 @@ impl TopLevelComposer {
|
|||
})
|
||||
};
|
||||
let mut identifiers = {
|
||||
// NOTE: none and function args?
|
||||
let mut result: HashSet<_> = HashSet::new();
|
||||
result.insert("None".into());
|
||||
if self_type.is_some() {
|
||||
result.insert("self".into());
|
||||
}
|
||||
|
@ -1808,9 +1807,7 @@ impl TopLevelComposer {
|
|||
},
|
||||
unifier,
|
||||
variable_mapping: {
|
||||
// NOTE: none and function args?
|
||||
let mut result: HashMap<StrRef, Type> = HashMap::new();
|
||||
result.insert("None".into(), primitives_ty.none);
|
||||
if let Some(self_ty) = self_type {
|
||||
result.insert("self".into(), self_ty);
|
||||
}
|
||||
|
|
|
@ -107,7 +107,43 @@ impl TopLevelComposer {
|
|||
fields: 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);
|
||||
(primitives, unifier)
|
||||
}
|
||||
|
@ -380,76 +416,77 @@ impl TopLevelComposer {
|
|||
primitive: &PrimitiveStore,
|
||||
unifier: &mut Unifier,
|
||||
) -> Result<(), String> {
|
||||
let res = match val {
|
||||
SymbolValue::Bool(..) => {
|
||||
if matches!(ty, TypeAnnotation::Primitive(t) if *t == primitive.bool) {
|
||||
None
|
||||
} else {
|
||||
Some("bool".to_string())
|
||||
fn type_default_param(
|
||||
val: &SymbolValue,
|
||||
primitive: &PrimitiveStore,
|
||||
unifier: &mut Unifier,
|
||||
) -> TypeAnnotation {
|
||||
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::Double(..) => {
|
||||
if matches!(ty, TypeAnnotation::Primitive(t) if *t == primitive.float) {
|
||||
None
|
||||
} else {
|
||||
Some("float".to_string())
|
||||
}
|
||||
}
|
||||
SymbolValue::I32(..) => {
|
||||
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)?
|
||||
SymbolValue::OptionNone => TypeAnnotation::CustomClass {
|
||||
id: primitive.option.get_obj_id(unifier),
|
||||
params: Default::default(),
|
||||
},
|
||||
SymbolValue::OptionSome(v) => {
|
||||
let ty = type_default_param(v, primitive, unifier);
|
||||
TypeAnnotation::CustomClass {
|
||||
id: primitive.option.get_obj_id(unifier),
|
||||
params: vec![ty],
|
||||
}
|
||||
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!(
|
||||
"incompatible default parameter type, expect {}, found {}",
|
||||
ty.stringify(unifier),
|
||||
found
|
||||
found.stringify(unifier),
|
||||
))
|
||||
} else {
|
||||
Ok(())
|
||||
|
@ -475,6 +512,10 @@ pub fn parse_parameter_default_value(
|
|||
Constant::Tuple(tuple) => Ok(SymbolValue::Tuple(
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
@ -512,6 +553,11 @@ pub fn parse_parameter_default_value(
|
|||
}
|
||||
_ => 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)),
|
||||
}
|
||||
}
|
||||
|
@ -520,15 +566,20 @@ pub fn parse_parameter_default_value(
|
|||
.map(|x| parse_parameter_default_value(x, resolver))
|
||||
.collect::<Result<Vec<_>, _>>()?
|
||||
)),
|
||||
ast::ExprKind::Name { id, .. } if id == &"none".into() => Ok(SymbolValue::OptionNone),
|
||||
ast::ExprKind::Name { id, .. } => {
|
||||
resolver.get_default_param_value(default).ok_or_else(
|
||||
|| 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,
|
||||
default.location
|
||||
)
|
||||
)
|
||||
}
|
||||
_ => Err(format!("unsupported default parameter at {}", default.location))
|
||||
_ => Err(format!(
|
||||
"unsupported default parameter (not primitive type, option or tuple) at {}",
|
||||
default.location
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.fun\",\nsig: \"fn[[a:int32], V]\",\nvar_id: [17]\n}\n",
|
||||
"Class {\nname: \"B\",\nancestors: [\"{class: B, params: []}\"],\nfields: [\"aa\"],\nmethods: [(\"__init__\", \"fn[[], none]\"), (\"foo\", \"fn[[b:T], none]\")],\ntype_vars: []\n}\n",
|
||||
"Function {\nname: \"Generic_A.fun\",\nsig: \"fn[[a:int32], V]\",\nvar_id: [18]\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.foo\",\nsig: \"fn[[b:T], none]\",\nvar_id: []\n}\n",
|
||||
]
|
||||
|
|
|
@ -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.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",
|
||||
"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.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",
|
||||
]
|
||||
|
|
|
@ -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",
|
||||
"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",
|
||||
"Function {\nname: \"A.__init__\",\nsig: \"fn[[v:V], none]\",\nvar_id: [19]\n}\n",
|
||||
"Function {\nname: \"A.fun\",\nsig: \"fn[[a:T], V]\",\nvar_id: [24]\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: [20]\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",
|
||||
"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",
|
||||
]
|
||||
|
|
|
@ -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.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.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",
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
---
|
||||
source: nac3core/src/toplevel/test.rs
|
||||
assertion_line: 540
|
||||
assertion_line: 549
|
||||
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.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",
|
||||
"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",
|
||||
"Function {\nname: \"A.foo\",\nsig: \"fn[[a:T, b:V], none]\",\nvar_id: [26]\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",
|
||||
"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.fun\",\nsig: \"fn[[b:B], 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",
|
||||
]
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
---
|
||||
source: nac3core/src/toplevel/test.rs
|
||||
assertion_line: 541
|
||||
assertion_line: 549
|
||||
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",
|
||||
]
|
||||
|
|
|
@ -65,14 +65,14 @@ impl SymbolResolver for Resolver {
|
|||
}
|
||||
|
||||
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 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_exception_id(&self, tyid: usize) -> usize {
|
||||
fn get_exception_id(&self, _tyid: usize) -> usize {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,20 +29,28 @@ impl TypeAnnotation {
|
|||
{
|
||||
(*name).into()
|
||||
} else {
|
||||
format!("def_{}", id.0)
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
None => format!("def_{}", id.0),
|
||||
None => format!("class_def_{}", id.0),
|
||||
};
|
||||
format!(
|
||||
"{{class: {}, params: {:?}}}",
|
||||
"{}{}",
|
||||
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) => {
|
||||
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))
|
||||
} else if id == &"bool".into() {
|
||||
Ok(TypeAnnotation::Primitive(primitives.bool))
|
||||
} else if id == &"None".into() {
|
||||
Ok(TypeAnnotation::Primitive(primitives.none))
|
||||
} else if id == &"str".into() {
|
||||
Ok(TypeAnnotation::Primitive(primitives.str))
|
||||
} else if id == &"Exception".into() {
|
||||
|
@ -223,6 +229,29 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
|
|||
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
|
||||
ast::ExprKind::Subscript { value, slice, .. }
|
||||
if {
|
||||
|
|
|
@ -20,6 +20,8 @@ impl<'a> Inferencer<'a> {
|
|||
defined_identifiers: &mut HashSet<StrRef>,
|
||||
) -> Result<(), String> {
|
||||
match &pattern.node {
|
||||
ast::ExprKind::Name { id, .. } if id == &"none".into() =>
|
||||
Err(format!("cannot assign to a `none` (at {})", pattern.location)),
|
||||
ExprKind::Name { id, .. } => {
|
||||
if !defined_identifiers.contains(id) {
|
||||
defined_identifiers.insert(*id);
|
||||
|
@ -70,6 +72,9 @@ impl<'a> Inferencer<'a> {
|
|||
}
|
||||
match &expr.node {
|
||||
ExprKind::Name { id, .. } => {
|
||||
if id == &"none".into() {
|
||||
return Ok(());
|
||||
}
|
||||
self.should_have_value(expr)?;
|
||||
if !defined_identifiers.contains(id) {
|
||||
match self.function_data.resolver.get_symbol_type(
|
||||
|
|
|
@ -40,6 +40,7 @@ pub struct PrimitiveStore {
|
|||
pub range: Type,
|
||||
pub str: Type,
|
||||
pub exception: Type,
|
||||
pub option: Type,
|
||||
}
|
||||
|
||||
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)?;
|
||||
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),
|
||||
};
|
||||
Ok(stmt)
|
||||
|
@ -448,25 +456,47 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
|
|||
Some(self.infer_constant(value, &expr.location)?)
|
||||
}
|
||||
ast::ExprKind::Name { id, .. } => {
|
||||
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,
|
||||
);
|
||||
// the name `none` is special since it may have different types
|
||||
if id == &"none".into() {
|
||||
if let TypeEnum::TObj { params, .. } =
|
||||
self.unifier.get_ty_immutable(self.primitives.option).as_ref()
|
||||
{
|
||||
let var_map = params
|
||||
.iter()
|
||||
.map(|(id_var, ty)| {
|
||||
if let TypeEnum::TVar { id, range, name, loc, .. } = &*self.unifier.get_ty(*ty) {
|
||||
assert_eq!(*id, *id_var);
|
||||
(*id, self.unifier.get_fresh_var_with_range(range, *name, *loc).0)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
})
|
||||
.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::Tuple { elts, .. } => Some(self.infer_tuple(elts)?),
|
||||
|
@ -932,6 +962,8 @@ impl<'a> Inferencer<'a> {
|
|||
Ok(self.unifier.add_ty(TypeEnum::TTuple { ty: ty? }))
|
||||
}
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,14 +44,14 @@ impl SymbolResolver for Resolver {
|
|||
}
|
||||
|
||||
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 {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn get_exception_id(&self, tyid: usize) -> usize {
|
||||
fn get_exception_id(&self, _tyid: usize) -> usize {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
@ -129,7 +129,24 @@ impl TestEnvironment {
|
|||
fields: 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);
|
||||
|
||||
let id_to_name = [
|
||||
|
@ -237,6 +254,11 @@ impl TestEnvironment {
|
|||
fields: 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);
|
||||
for (i, name) in ["int32", "int64", "float", "bool", "none", "range", "str", "Exception"]
|
||||
.iter()
|
||||
|
@ -259,7 +281,19 @@ impl TestEnvironment {
|
|||
}
|
||||
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();
|
||||
|
||||
|
|
|
@ -54,6 +54,18 @@ pub enum RecordKey {
|
|||
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 {
|
||||
fn from(r: &RecordKey) -> Self {
|
||||
match r {
|
||||
|
|
|
@ -121,7 +121,7 @@ mod tests {
|
|||
#[test]
|
||||
fn test_parse_lambda() {
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -129,7 +129,7 @@ mod tests {
|
|||
fn test_parse_tuples() {
|
||||
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]
|
||||
|
@ -140,7 +140,7 @@ class Foo(A, B):
|
|||
pass
|
||||
def method_with_default(self, arg='default'):
|
||||
pass";
|
||||
insta::assert_debug_snapshot!(parse_program(&source, Default::default()).unwrap());
|
||||
insta::assert_debug_snapshot!(parse_program(source, Default::default()).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -183,7 +183,7 @@ while i < 2: # nac3: 4
|
|||
# nac3: if1
|
||||
if 1: # nac3: if2
|
||||
3";
|
||||
insta::assert_debug_snapshot!(parse_program(&source, Default::default()).unwrap());
|
||||
insta::assert_debug_snapshot!(parse_program(source, Default::default()).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -196,7 +196,7 @@ while test: # nac3: while3
|
|||
# nac3: simple assign0
|
||||
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]
|
||||
|
@ -215,7 +215,7 @@ if a: # nac3: small2
|
|||
for i in a: # nac3: for1
|
||||
pass
|
||||
";
|
||||
insta::assert_debug_snapshot!(parse_program(&source, Default::default()).unwrap());
|
||||
insta::assert_debug_snapshot!(parse_program(source, Default::default()).unwrap());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -224,6 +224,6 @@ for i in a: # nac3: for1
|
|||
if a: # nac3: something
|
||||
a = 3
|
||||
";
|
||||
assert!(parse_program(&source, Default::default()).is_err());
|
||||
assert!(parse_program(source, Default::default()).is_err());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -74,6 +74,11 @@ pub extern "C" fn __nac3_personality(_state: u32, _exception_object: u32, _conte
|
|||
unimplemented!();
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn __nac3_raise(_state: u32, _exception_object: u32, _context: u32) -> u32 {
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn run() -> i32;
|
||||
}
|
||||
|
|
|
@ -8,6 +8,38 @@ import pathlib
|
|||
from numpy import int32, int64, uint32, uint64
|
||||
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 output_asciiart(x):
|
||||
|
@ -39,6 +71,9 @@ def patch(module):
|
|||
module.TypeVar = TypeVar
|
||||
module.Generic = Generic
|
||||
module.extern = extern
|
||||
module.Option = Option
|
||||
module.Some = Some
|
||||
module.none = none
|
||||
|
||||
|
||||
def file_import(filename, prefix="file_import_"):
|
||||
|
|
|
@ -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
|
|
@ -0,0 +1,9 @@
|
|||
@extern
|
||||
def output_int32(x: int32):
|
||||
...
|
||||
|
||||
def run() -> int32:
|
||||
for _ in range(10):
|
||||
output_int32(_)
|
||||
_ = 0
|
||||
return 0
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -33,13 +33,14 @@ let
|
|||
};
|
||||
in rec {
|
||||
llvm-nac3 = pkgs.stdenvNoCC.mkDerivation rec {
|
||||
name = "llvm-nac3-msys2";
|
||||
pname = "llvm-nac3-msys2";
|
||||
version = "13.0.1";
|
||||
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=";
|
||||
};
|
||||
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=";
|
||||
};
|
||||
buildInputs = [ pkgs.wineWowPackages.stable ];
|
||||
|
@ -73,11 +74,12 @@ in rec {
|
|||
''
|
||||
wine64 ninja install
|
||||
'';
|
||||
dontFixup = true;
|
||||
};
|
||||
nac3artiq = pkgs.rustPlatform.buildRustPackage {
|
||||
name = "nac3artiq";
|
||||
src = ../.;
|
||||
cargoLock = { lockFile = ../Cargo.lock; };
|
||||
name = "nac3artiq-msys2";
|
||||
src = ../../.;
|
||||
cargoLock = { lockFile = ../../Cargo.lock; };
|
||||
nativeBuildInputs = [ pkgs.wineWowPackages.stable pkgs.zip ];
|
||||
buildPhase =
|
||||
''
|
||||
|
@ -101,6 +103,38 @@ in rec {
|
|||
'';
|
||||
dontFixup = true;
|
||||
};
|
||||
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"
|
||||
''
|
||||
export WINEDEBUG=-all
|
|
@ -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:
|
Loading…
Reference in New Issue