forked from M-Labs/nac3
Compare commits
30 Commits
escape-ana
...
master
Author | SHA1 | Date |
---|---|---|
wylited | 35b6459c58 | |
wylited | e94b25f544 | |
Sebastien Bourdeauducq | 6972689469 | |
Sebastien Bourdeauducq | 3fb22c9182 | |
Sebastien Bourdeauducq | 1e7abf0268 | |
Sebastien Bourdeauducq | f5a6d29106 | |
Sebastien Bourdeauducq | ca07cb66cd | |
Sebastien Bourdeauducq | 93e9a6a38a | |
ychenfo | 722e3df086 | |
ychenfo | ad9ad22cb8 | |
ychenfo | f66f66b3a4 | |
ychenfo | 6c485bc9dc | |
ychenfo | 089bba96a3 | |
ychenfo | 0e0871bc38 | |
ychenfo | 26187bff0b | |
ychenfo | 86ce513cb5 | |
ychenfo | c29cbf6ddd | |
ychenfo | 7443c5ea0f | |
Sebastien Bourdeauducq | f55b077e60 | |
Sebastien Bourdeauducq | e05b0bf5dc | |
Sebastien Bourdeauducq | 8eda0affc9 | |
Sebastien Bourdeauducq | 75c53b40a3 | |
pca006132 | 0d10044d66 | |
ychenfo | 23b7f4ef18 | |
ychenfo | 710904f975 | |
Sebastien Bourdeauducq | 4bf452ec5a | |
Sebastien Bourdeauducq | 9fdce11efe | |
Sebastien Bourdeauducq | f24ef85aed | |
Sebastien Bourdeauducq | 4a19787f10 | |
Sebastien Bourdeauducq | 8209c0a475 |
|
@ -422,9 +422,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.121"
|
||||
version = "0.2.122"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f"
|
||||
checksum = "ec647867e2bf0772e28c8bcde4f0d19a9216916e890543b5a03ed8ef27b8f259"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
|
@ -524,7 +524,6 @@ dependencies = [
|
|||
"parking_lot 0.11.2",
|
||||
"rayon",
|
||||
"regex",
|
||||
"slab",
|
||||
"test-case",
|
||||
]
|
||||
|
||||
|
@ -741,9 +740,9 @@ checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.36"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
|
||||
checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
@ -798,9 +797,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.17"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "632d02bff7f874a36f33ea8bb416cd484b90cc66c1194b1a1110d067a7013f58"
|
||||
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
@ -1004,12 +1003,6 @@ version = "0.3.10"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7bd3e3206899af3f8b12af284fafc038cc1dc2b41d1b89dd17297221c5d225de"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb703cfe953bccee95685111adeedb76fabe4e97549a58d16f03ea7b9367bb32"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.8.0"
|
||||
|
@ -1042,9 +1035,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.90"
|
||||
version = "1.0.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "704df27628939572cd88d33f171cd6f896f4eaca85252c6e0a72d8d8287ee86f"
|
||||
checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
33
README.md
33
README.md
|
@ -16,42 +16,27 @@ NAC3 is packaged using the [Nix](https://nixos.org) Flakes system. Install Nix 2
|
|||
|
||||
After setting up Nix as above, use ``nix shell git+https://github.com/m-labs/artiq.git?ref=nac3`` to get a shell with the NAC3 version of ARTIQ. See the ``examples`` directory in ARTIQ (``nac3`` Git branch) for some samples of NAC3 kernel code.
|
||||
|
||||
### Windows (work in progress)
|
||||
### Windows
|
||||
|
||||
NAC3 ARTIQ packaging for MSYS2/Windows is not yet complete so installation involves many manual steps. It is also less tested and you may encounter problems.
|
||||
|
||||
Install [MSYS2](https://www.msys2.org/) and run the following commands:
|
||||
Install [MSYS2](https://www.msys2.org/), and open "MSYS2 MinGW x64". Edit ``/etc/pacman.conf`` to add:
|
||||
```
|
||||
pacman -S mingw-w64-x86_64-python-h5py mingw-w64-x86_64-python-pyqt5 mingw-w64-x86_64-python-scipy mingw-w64-x86_64-python-prettytable mingw-w64-x86_64-python-pygit2
|
||||
pacman -S mingw-w64-x86_64-python-pip
|
||||
pip install qasync
|
||||
pip install pyqtgraph
|
||||
pacman -S patch git
|
||||
git clone https://github.com/m-labs/sipyco
|
||||
cd sipyco
|
||||
git show 20c946aad78872fe60b78d9b57a624d69f3eea47 | patch -p1 -R
|
||||
python setup.py install
|
||||
cd ..
|
||||
git clone -b nac3 https://github.com/m-labs/artiq
|
||||
cd artiq
|
||||
python setup.py install
|
||||
[artiq]
|
||||
SigLevel = Optional TrustAll
|
||||
Server = https://lab.m-labs.hk/msys2
|
||||
```
|
||||
|
||||
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:
|
||||
Then run the following commands:
|
||||
```
|
||||
pacman -S unzip
|
||||
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
|
||||
pacman -Syu
|
||||
pacman -S mingw-w64-x86_64-artiq
|
||||
```
|
||||
|
||||
Do the same for ``lld-msys2``:
|
||||
Install ``lld-msys2`` manually:
|
||||
```
|
||||
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
|
||||
|
|
|
@ -2,11 +2,11 @@
|
|||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1648553562,
|
||||
"narHash": "sha256-xQhRKu6h0phd56oCzGjkhHkY4eDI1XKedGqkFtlXapk=",
|
||||
"lastModified": 1649619156,
|
||||
"narHash": "sha256-p0q4zpuKMwrzGF+5ZU7Thnpac5TinhDI9jr2mBxhV4w=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "9b168e5e62406fa2e55e132f390379a6ba22b402",
|
||||
"rev": "e7d63bd0d50df412f5a1d8acfa3caae75522e347",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
@ -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;
|
||||
nac3artiq-msys2-pkg = packages.x86_64-w64-mingw32.nac3artiq-pkg;
|
||||
lld-msys2 = packages.x86_64-w64-mingw32.lld;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,10 +1,4 @@
|
|||
from min_artiq import *
|
||||
from numpy import int32, int64
|
||||
|
||||
|
||||
@extern
|
||||
def output_int(x: int32):
|
||||
...
|
||||
|
||||
|
||||
@nac3
|
||||
|
|
|
@ -68,7 +68,7 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
|
|||
) -> Result<Option<BasicValueEnum<'ctx>>, String> {
|
||||
let result = gen_call(self, ctx, obj, fun, params)?;
|
||||
if let Some(end) = self.end.clone() {
|
||||
let old_end = self.gen_expr(ctx, &end)?.unwrap().to_basic_value_enum(ctx, self)?;
|
||||
let old_end = self.gen_expr(ctx, &end)?.unwrap().to_basic_value_enum(ctx, self, end.custom.unwrap())?;
|
||||
let now = self.timeline.emit_now_mu(ctx);
|
||||
let smax = ctx.module.get_function("llvm.smax.i64").unwrap_or_else(|| {
|
||||
let i64 = ctx.ctx.i64_type();
|
||||
|
@ -88,7 +88,7 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
|
|||
ctx.builder.build_store(end_store, max);
|
||||
}
|
||||
if let Some(start) = self.start.clone() {
|
||||
let start_val = self.gen_expr(ctx, &start)?.unwrap().to_basic_value_enum(ctx, self)?;
|
||||
let start_val = self.gen_expr(ctx, &start)?.unwrap().to_basic_value_enum(ctx, self, start.custom.unwrap())?;
|
||||
self.timeline.emit_at_mu(ctx, start_val);
|
||||
}
|
||||
Ok(result)
|
||||
|
@ -120,7 +120,7 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
|
|||
let old_start = self.start.take();
|
||||
let old_end = self.end.take();
|
||||
let now = if let Some(old_start) = &old_start {
|
||||
self.gen_expr(ctx, old_start)?.unwrap().to_basic_value_enum(ctx, self)?
|
||||
self.gen_expr(ctx, old_start)?.unwrap().to_basic_value_enum(ctx, self, old_start.custom.unwrap())?
|
||||
} else {
|
||||
self.timeline.emit_now_mu(ctx)
|
||||
};
|
||||
|
@ -174,8 +174,10 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
|
|||
};
|
||||
// set duration
|
||||
let end_expr = self.end.take().unwrap();
|
||||
let end_val =
|
||||
self.gen_expr(ctx, &end_expr)?.unwrap().to_basic_value_enum(ctx, self)?;
|
||||
let end_val = self
|
||||
.gen_expr(ctx, &end_expr)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, self, end_expr.custom.unwrap())?;
|
||||
|
||||
// inside a sequential block
|
||||
if old_start.is_none() {
|
||||
|
@ -186,7 +188,7 @@ impl<'b> CodeGenerator for ArtiqCodeGenerator<'b> {
|
|||
let outer_end_val = self
|
||||
.gen_expr(ctx, old_end)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, self)?;
|
||||
.to_basic_value_enum(ctx, self, old_end.custom.unwrap())?;
|
||||
let smax =
|
||||
ctx.module.get_function("llvm.smax.i64").unwrap_or_else(|| {
|
||||
let i64 = ctx.ctx.i64_type();
|
||||
|
@ -371,7 +373,7 @@ fn rpc_codegen_callback_fn<'ctx, 'a>(
|
|||
.0
|
||||
.args
|
||||
.iter()
|
||||
.map(|arg| mapping.remove(&arg.name).unwrap().to_basic_value_enum(ctx, generator))
|
||||
.map(|arg| mapping.remove(&arg.name).unwrap().to_basic_value_enum(ctx, generator, arg.ty))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
if let Some(obj) = obj {
|
||||
if let ValueEnum::Static(obj) = obj.1 {
|
||||
|
@ -511,11 +513,13 @@ pub fn attributes_writeback<'ctx, 'a>(
|
|||
}
|
||||
let ty = ty.unwrap();
|
||||
match &*ctx.unifier.get_ty(ty) {
|
||||
TypeEnum::TObj { fields, .. } => {
|
||||
TypeEnum::TObj { fields, obj_id, .. }
|
||||
if *obj_id != ctx.primitives.option.get_obj_id(&ctx.unifier) =>
|
||||
{
|
||||
// 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();
|
||||
let obj = inner_resolver.get_obj_value(py, val, ctx, generator, ty)?.unwrap();
|
||||
for (name, (field_ty, is_mutable)) in fields.iter() {
|
||||
if !is_mutable {
|
||||
continue
|
||||
|
@ -540,7 +544,7 @@ pub fn attributes_writeback<'ctx, 'a>(
|
|||
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()));
|
||||
values.push((ty, inner_resolver.get_obj_value(py, val, ctx, generator, ty)?.unwrap()));
|
||||
}
|
||||
},
|
||||
_ => {}
|
||||
|
|
|
@ -65,6 +65,7 @@ pub struct PrimitivePythonId {
|
|||
uint32: u64,
|
||||
uint64: u64,
|
||||
float: u64,
|
||||
float64: u64,
|
||||
bool: u64,
|
||||
list: u64,
|
||||
tuple: u64,
|
||||
|
@ -328,8 +329,9 @@ impl Nac3 {
|
|||
ret: primitive.none,
|
||||
vars: HashMap::new(),
|
||||
},
|
||||
Arc::new(GenCall::new(Box::new(move |ctx, _, _, args, generator| {
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator).unwrap();
|
||||
Arc::new(GenCall::new(Box::new(move |ctx, _, fun, args, generator| {
|
||||
let arg_ty = fun.0.args[0].ty;
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty).unwrap();
|
||||
time_fns.emit_at_mu(ctx, arg);
|
||||
Ok(None)
|
||||
}))),
|
||||
|
@ -345,8 +347,9 @@ impl Nac3 {
|
|||
ret: primitive.none,
|
||||
vars: HashMap::new(),
|
||||
},
|
||||
Arc::new(GenCall::new(Box::new(move |ctx, _, _, args, generator| {
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator).unwrap();
|
||||
Arc::new(GenCall::new(Box::new(move |ctx, _, fun, args, generator| {
|
||||
let arg_ty = fun.0.args[0].ty;
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty).unwrap();
|
||||
time_fns.emit_delay_mu(ctx, arg);
|
||||
Ok(None)
|
||||
}))),
|
||||
|
@ -395,6 +398,7 @@ impl Nac3 {
|
|||
uint64: get_attr_id(numpy_mod, "uint64"),
|
||||
bool: get_attr_id(builtins_mod, "bool"),
|
||||
float: get_attr_id(builtins_mod, "float"),
|
||||
float64: get_attr_id(numpy_mod, "float64"),
|
||||
list: get_attr_id(builtins_mod, "list"),
|
||||
tuple: get_attr_id(builtins_mod, "tuple"),
|
||||
exception: get_attr_id(builtins_mod, "Exception"),
|
||||
|
|
|
@ -131,6 +131,7 @@ impl StaticValue for PythonValue {
|
|||
&self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
expected_ty: Type,
|
||||
) -> Result<BasicValueEnum<'ctx>, String> {
|
||||
if let Some(val) = self.resolver.id_to_primitive.read().get(&self.id) {
|
||||
return Ok(match val {
|
||||
|
@ -150,7 +151,7 @@ impl StaticValue for PythonValue {
|
|||
|
||||
Python::with_gil(|py| -> PyResult<BasicValueEnum<'ctx>> {
|
||||
self.resolver
|
||||
.get_obj_value(py, self.value.as_ref(py), ctx, generator)
|
||||
.get_obj_value(py, self.value.as_ref(py), ctx, generator, expected_ty)
|
||||
.map(Option::unwrap)
|
||||
}).map_err(|e| e.to_string())
|
||||
}
|
||||
|
@ -169,6 +170,16 @@ impl StaticValue for PythonValue {
|
|||
let helper = &self.resolver.helper;
|
||||
let ty = helper.type_fn.call1(py, (&self.value,))?;
|
||||
let ty_id: u64 = helper.id_fn.call1(py, (ty,))?.extract(py)?;
|
||||
// for optimizing unwrap KernelInvariant
|
||||
if ty_id == self.resolver.primitive_ids.option && name == "_nac3_option".into() {
|
||||
let obj = self.value.getattr(py, &name.to_string())?;
|
||||
let id = self.resolver.helper.id_fn.call1(py, (&obj,))?.extract(py)?;
|
||||
if self.id == self.resolver.primitive_ids.none {
|
||||
return Ok(None)
|
||||
} else {
|
||||
return Ok(Some((id, obj)))
|
||||
}
|
||||
}
|
||||
let def_id = { *self.resolver.pyid_to_def.read().get(&ty_id).unwrap() };
|
||||
let mut mutable = true;
|
||||
let defs = ctx.top_level.definitions.read();
|
||||
|
@ -201,6 +212,28 @@ impl StaticValue for PythonValue {
|
|||
}))
|
||||
})
|
||||
}
|
||||
|
||||
fn get_tuple_element<'ctx>(&self, index: u32) -> Option<ValueEnum<'ctx>> {
|
||||
Python::with_gil(|py| -> PyResult<Option<(u64, PyObject)>> {
|
||||
let helper = &self.resolver.helper;
|
||||
let ty = helper.type_fn.call1(py, (&self.value,))?;
|
||||
let ty_id: u64 = helper.id_fn.call1(py, (ty,))?.extract(py)?;
|
||||
assert_eq!(ty_id, self.resolver.primitive_ids.tuple);
|
||||
let tup: &PyTuple = self.value.extract(py)?;
|
||||
let elem = tup.get_item(index as usize);
|
||||
let id = self.resolver.helper.id_fn.call1(py, (elem,))?.extract(py)?;
|
||||
Ok(Some((id, elem.into())))
|
||||
})
|
||||
.unwrap()
|
||||
.map(|(id, obj)| {
|
||||
ValueEnum::Static(Arc::new(PythonValue {
|
||||
id,
|
||||
value: obj,
|
||||
store_obj: self.store_obj.clone(),
|
||||
resolver: self.resolver.clone(),
|
||||
}))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl InnerResolver {
|
||||
|
@ -269,6 +302,8 @@ impl InnerResolver {
|
|||
Ok(Ok((primitives.bool, true)))
|
||||
} else if ty_id == self.primitive_ids.float {
|
||||
Ok(Ok((primitives.float, true)))
|
||||
} else if ty_id == self.primitive_ids.float64 {
|
||||
Ok(Ok((primitives.float, true)))
|
||||
} else if ty_id == self.primitive_ids.exception {
|
||||
Ok(Ok((primitives.exception, true)))
|
||||
} else if ty_id == self.primitive_ids.list {
|
||||
|
@ -544,7 +579,7 @@ impl InnerResolver {
|
|||
let len: usize = self.helper.len_fn.call1(py, (obj,))?.extract(py)?;
|
||||
if len == 0 {
|
||||
assert!(matches!(
|
||||
&*unifier.get_ty(extracted_ty),
|
||||
&*unifier.get_ty(*ty),
|
||||
TypeEnum::TVar { fields: None, range, .. }
|
||||
if range.is_empty()
|
||||
));
|
||||
|
@ -692,6 +727,7 @@ impl InnerResolver {
|
|||
obj: &PyAny,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
expected_ty: Type,
|
||||
) -> PyResult<Option<BasicValueEnum<'ctx>>> {
|
||||
let ty_id: u64 =
|
||||
self.helper.id_fn.call1(py, (self.helper.type_fn.call1(py, (obj,))?,))?.extract(py)?;
|
||||
|
@ -721,7 +757,7 @@ impl InnerResolver {
|
|||
format!("{} is not in the range of bool", obj)))?;
|
||||
self.id_to_primitive.write().insert(id, PrimitiveValue::Bool(val));
|
||||
Ok(Some(ctx.ctx.bool_type().const_int(val as u64, false).into()))
|
||||
} else if ty_id == self.primitive_ids.float {
|
||||
} else if ty_id == self.primitive_ids.float || ty_id == self.primitive_ids.float64 {
|
||||
let val: f64 = obj.extract().map_err(|_| super::CompileError::new_err(
|
||||
format!("{} is not in the range of float64", obj)))?;
|
||||
self.id_to_primitive.write().insert(id, PrimitiveValue::F64(val));
|
||||
|
@ -734,20 +770,14 @@ impl InnerResolver {
|
|||
}
|
||||
|
||||
let len: usize = self.helper.len_fn.call1(py, (obj,))?.extract(py)?;
|
||||
let ty = if len == 0 {
|
||||
ctx.primitives.int32
|
||||
let elem_ty =
|
||||
if let TypeEnum::TList { ty } = ctx.unifier.get_ty_immutable(expected_ty).as_ref()
|
||||
{
|
||||
*ty
|
||||
} else {
|
||||
self.get_list_elem_type(
|
||||
py,
|
||||
obj,
|
||||
len,
|
||||
&mut ctx.unifier,
|
||||
&ctx.top_level.definitions.read(),
|
||||
&ctx.primitives,
|
||||
)?
|
||||
.unwrap()
|
||||
unreachable!("must be list")
|
||||
};
|
||||
let ty = ctx.get_llvm_type(generator, ty);
|
||||
let ty = ctx.get_llvm_type(generator, elem_ty);
|
||||
let size_t = generator.get_size_type(ctx.ctx);
|
||||
let arr_ty = ctx
|
||||
.ctx
|
||||
|
@ -766,8 +796,13 @@ impl InnerResolver {
|
|||
|
||||
let arr: Result<Option<Vec<_>>, _> = (0..len)
|
||||
.map(|i| {
|
||||
obj.get_item(i).and_then(|elem| self.get_obj_value(py, elem, ctx, generator).map_err(
|
||||
|e| super::CompileError::new_err(format!("Error getting element {}: {}", i, e))))
|
||||
obj
|
||||
.get_item(i)
|
||||
.and_then(|elem| self.get_obj_value(py, elem, ctx, generator, elem_ty)
|
||||
.map_err(
|
||||
|e| super::CompileError::new_err(
|
||||
format!("Error getting element {}: {}", i, e))
|
||||
))
|
||||
})
|
||||
.collect();
|
||||
let arr = arr?.unwrap();
|
||||
|
@ -808,21 +843,48 @@ impl InnerResolver {
|
|||
|
||||
Ok(Some(global.as_pointer_value().into()))
|
||||
} else if ty_id == self.primitive_ids.tuple {
|
||||
let elements: &PyTuple = obj.cast_as()?;
|
||||
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);
|
||||
Ok(Some(val.into()))
|
||||
if let TypeEnum::TTuple { ty } = ctx.unifier.get_ty_immutable(expected_ty).as_ref() {
|
||||
let tup_tys = ty.iter();
|
||||
let elements: &PyTuple = obj.cast_as()?;
|
||||
assert_eq!(elements.len(), tup_tys.len());
|
||||
let val: Result<Option<Vec<_>>, _> =
|
||||
elements
|
||||
.iter()
|
||||
.enumerate()
|
||||
.zip(tup_tys)
|
||||
.map(|((i, elem), ty)| self
|
||||
.get_obj_value(py, elem, ctx, generator, *ty).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);
|
||||
Ok(Some(val.into()))
|
||||
} else {
|
||||
unreachable!("must expect tuple type")
|
||||
}
|
||||
} else if ty_id == self.primitive_ids.option {
|
||||
let option_val_ty = match ctx.unifier.get_ty_immutable(expected_ty).as_ref() {
|
||||
TypeEnum::TObj { obj_id, params, .. }
|
||||
if *obj_id == ctx.primitives.option.get_obj_id(&ctx.unifier) =>
|
||||
{
|
||||
*params.iter().next().unwrap().1
|
||||
}
|
||||
_ => unreachable!("must be option type")
|
||||
};
|
||||
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()))
|
||||
// for option type, just a null ptr
|
||||
Ok(Some(
|
||||
ctx.get_llvm_type(generator, option_val_ty)
|
||||
.ptr_type(AddressSpace::Generic)
|
||||
.const_null()
|
||||
.into(),
|
||||
))
|
||||
} else {
|
||||
match self
|
||||
.get_obj_value(py, obj.getattr("_nac3_option").unwrap(), ctx, generator)
|
||||
.get_obj_value(py, obj.getattr("_nac3_option").unwrap(), ctx, generator, option_val_ty)
|
||||
.map_err(|e| {
|
||||
super::CompileError::new_err(format!(
|
||||
"Error getting value of Option object: {}",
|
||||
|
@ -881,23 +943,8 @@ impl InnerResolver {
|
|||
let values: Result<Option<Vec<_>>, _> = fields
|
||||
.iter()
|
||||
.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,
|
||||
}
|
||||
self.get_obj_value(py, obj.getattr(&name.to_string())?, ctx, generator, *ty)
|
||||
.map_err(|e| super::CompileError::new_err(format!("Error getting field {}: {}", name, e)))
|
||||
})
|
||||
.collect();
|
||||
let values = values?;
|
||||
|
@ -940,7 +987,7 @@ impl InnerResolver {
|
|||
} else if ty_id == self.primitive_ids.bool {
|
||||
let val: bool = obj.extract()?;
|
||||
Ok(SymbolValue::Bool(val))
|
||||
} else if ty_id == self.primitive_ids.float {
|
||||
} else if ty_id == self.primitive_ids.float || ty_id == self.primitive_ids.float64 {
|
||||
let val: f64 = obj.extract()?;
|
||||
Ok(SymbolValue::Double(val))
|
||||
} else if ty_id == self.primitive_ids.tuple {
|
||||
|
|
|
@ -9,7 +9,6 @@ itertools = "0.10.1"
|
|||
crossbeam = "0.8.1"
|
||||
parking_lot = "0.11.1"
|
||||
rayon = "1.5.1"
|
||||
slab = "0.4.6"
|
||||
nac3parser = { path = "../nac3parser" }
|
||||
|
||||
[dependencies.inkwell]
|
||||
|
|
|
@ -150,11 +150,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
};
|
||||
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",
|
||||
)
|
||||
actual_ptr_type.const_null().into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -238,6 +234,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
|
||||
pub fn gen_int_ops(
|
||||
&mut self,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
op: &Operator,
|
||||
lhs: BasicValueEnum<'ctx>,
|
||||
rhs: BasicValueEnum<'ctx>,
|
||||
|
@ -273,7 +270,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
(Operator::RShift, _) => self.builder.build_right_shift(lhs, rhs, true, "rshift").into(),
|
||||
(Operator::FloorDiv, true) => self.builder.build_int_signed_div(lhs, rhs, "floordiv").into(),
|
||||
(Operator::FloorDiv, false) => self.builder.build_int_unsigned_div(lhs, rhs, "floordiv").into(),
|
||||
(Operator::Pow, s) => integer_power(self, lhs, rhs, s).into(),
|
||||
(Operator::Pow, s) => integer_power(generator, self, lhs, rhs, s).into(),
|
||||
// special implementation?
|
||||
(Operator::MatMult, _) => unreachable!(),
|
||||
}
|
||||
|
@ -395,9 +392,9 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
self.gen_const(generator, &nac3parser::ast::Constant::Str(s.into()), self.primitives.str)
|
||||
}
|
||||
|
||||
pub fn raise_exn<G: CodeGenerator>(
|
||||
pub fn raise_exn(
|
||||
&mut self,
|
||||
generator: &mut G,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
name: &str,
|
||||
msg: BasicValueEnum<'ctx>,
|
||||
params: [Option<IntValue<'ctx>>; 3],
|
||||
|
@ -434,9 +431,9 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
gen_raise(generator, self, Some(&zelf.into()), loc);
|
||||
}
|
||||
|
||||
pub fn make_assert<G: CodeGenerator>(
|
||||
pub fn make_assert(
|
||||
&mut self,
|
||||
generator: &mut G,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
cond: IntValue<'ctx>,
|
||||
err_name: &str,
|
||||
err_msg: &str,
|
||||
|
@ -447,9 +444,9 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
|||
self.make_assert_impl(generator, cond, err_name, err_msg, params, loc)
|
||||
}
|
||||
|
||||
pub fn make_assert_impl<G: CodeGenerator>(
|
||||
pub fn make_assert_impl(
|
||||
&mut self,
|
||||
generator: &mut G,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
cond: IntValue<'ctx>,
|
||||
err_name: &str,
|
||||
err_msg: BasicValueEnum<'ctx>,
|
||||
|
@ -642,14 +639,14 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
|
|||
}
|
||||
// reorder the parameters
|
||||
let mut real_params =
|
||||
fun.0.args.iter().map(|arg| mapping.remove(&arg.name).unwrap()).collect_vec();
|
||||
fun.0.args.iter().map(|arg| (mapping.remove(&arg.name).unwrap(), arg.ty)).collect_vec();
|
||||
if let Some(obj) = &obj {
|
||||
real_params.insert(0, obj.1.clone());
|
||||
real_params.insert(0, (obj.1.clone(), obj.0));
|
||||
}
|
||||
let static_params = real_params
|
||||
.iter()
|
||||
.enumerate()
|
||||
.filter_map(|(i, v)| {
|
||||
.filter_map(|(i, (v, _))| {
|
||||
if let ValueEnum::Static(s) = v {
|
||||
Some((i, s.clone()))
|
||||
} else {
|
||||
|
@ -681,7 +678,7 @@ pub fn gen_call<'ctx, 'a, G: CodeGenerator>(
|
|||
};
|
||||
param_vals = real_params
|
||||
.into_iter()
|
||||
.map(|p| p.to_basic_value_enum(ctx, generator))
|
||||
.map(|(p, t)| p.to_basic_value_enum(ctx, generator, t))
|
||||
.collect::<Result<Vec<_>, String>>()?;
|
||||
instance_to_symbol.get(&key).cloned().ok_or_else(|| "".into())
|
||||
}
|
||||
|
@ -794,7 +791,7 @@ pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>(
|
|||
let cont_bb = ctx.ctx.append_basic_block(current, "cont");
|
||||
|
||||
let Comprehension { target, iter, ifs, .. } = &generators[0];
|
||||
let iter_val = generator.gen_expr(ctx, iter)?.unwrap().to_basic_value_enum(ctx, generator)?;
|
||||
let iter_val = generator.gen_expr(ctx, iter)?.unwrap().to_basic_value_enum(ctx, generator, iter.custom.unwrap())?;
|
||||
let int32 = ctx.ctx.i32_type();
|
||||
let size_t = generator.get_size_type(ctx.ctx);
|
||||
let zero_size_t = size_t.const_zero();
|
||||
|
@ -899,7 +896,7 @@ pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>(
|
|||
let result = generator
|
||||
.gen_expr(ctx, cond)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)?
|
||||
.to_basic_value_enum(ctx, generator, cond.custom.unwrap())?
|
||||
.into_int_value();
|
||||
let succ = ctx.ctx.append_basic_block(current, "then");
|
||||
ctx.builder.build_conditional_branch(result, succ, test_bb);
|
||||
|
@ -908,7 +905,7 @@ pub fn gen_comprehension<'ctx, 'a, G: CodeGenerator>(
|
|||
let elem = generator.gen_expr(ctx, elt)?.unwrap();
|
||||
let i = ctx.builder.build_load(index, "i").into_int_value();
|
||||
let elem_ptr = unsafe { ctx.builder.build_gep(list_content, &[i], "elem_ptr") };
|
||||
let val = elem.to_basic_value_enum(ctx, generator)?;
|
||||
let val = elem.to_basic_value_enum(ctx, generator, elt.custom.unwrap())?;
|
||||
ctx.builder.build_store(elem_ptr, val);
|
||||
ctx.builder
|
||||
.build_store(index, ctx.builder.build_int_add(i, size_t.const_int(1, false), "inc"));
|
||||
|
@ -933,30 +930,27 @@ pub fn gen_binop_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
) -> Result<ValueEnum<'ctx>, String> {
|
||||
let ty1 = ctx.unifier.get_representative(left.custom.unwrap());
|
||||
let ty2 = ctx.unifier.get_representative(right.custom.unwrap());
|
||||
let left = generator.gen_expr(ctx, left)?.unwrap().to_basic_value_enum(ctx, generator)?;
|
||||
let right = generator.gen_expr(ctx, right)?.unwrap().to_basic_value_enum(ctx, generator)?;
|
||||
let left = generator.gen_expr(ctx, left)?.unwrap().to_basic_value_enum(ctx, generator, left.custom.unwrap())?;
|
||||
let right = generator.gen_expr(ctx, right)?.unwrap().to_basic_value_enum(ctx, generator, right.custom.unwrap())?;
|
||||
|
||||
// we can directly compare the types, because we've got their representatives
|
||||
// which would be unchanged until further unification, which we would never do
|
||||
// when doing code generation for function instances
|
||||
Ok(if ty1 == ty2 && [ctx.primitives.int32, ctx.primitives.int64].contains(&ty1) {
|
||||
ctx.gen_int_ops(op, left, right, true)
|
||||
ctx.gen_int_ops(generator, op, left, right, true)
|
||||
} else if ty1 == ty2 && [ctx.primitives.uint32, ctx.primitives.uint64].contains(&ty1) {
|
||||
ctx.gen_int_ops(op, left, right, false)
|
||||
ctx.gen_int_ops(generator, op, left, right, false)
|
||||
} else if ty1 == ty2 && ctx.primitives.float == ty1 {
|
||||
ctx.gen_float_ops(op, left, right)
|
||||
} 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()
|
||||
|
@ -972,6 +966,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
expr: &Expr<Option<Type>>,
|
||||
) -> Result<Option<ValueEnum<'ctx>>, String> {
|
||||
ctx.current_loc = expr.location;
|
||||
let int32 = ctx.ctx.i32_type();
|
||||
let zero = int32.const_int(0, false);
|
||||
Ok(Some(match &expr.node {
|
||||
|
@ -1000,25 +995,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
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 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,
|
||||
}
|
||||
resolver.get_symbol_value(*id, ctx).unwrap()
|
||||
}
|
||||
},
|
||||
ExprKind::List { elts, .. } => {
|
||||
|
@ -1029,7 +1006,10 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
.map(|x| {
|
||||
generator
|
||||
.gen_expr(ctx, x)
|
||||
.map_or_else(Err, |v| v.unwrap().to_basic_value_enum(ctx, generator))
|
||||
.map_or_else(
|
||||
Err,
|
||||
|v| v.unwrap().to_basic_value_enum(ctx, generator, x.custom.unwrap())
|
||||
)
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let ty = if elements.is_empty() {
|
||||
|
@ -1062,7 +1042,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
.map(|x| {
|
||||
generator
|
||||
.gen_expr(ctx, x)
|
||||
.map_or_else(Err, |v| v.unwrap().to_basic_value_enum(ctx, generator))
|
||||
.map_or_else(Err, |v| v.unwrap().to_basic_value_enum(ctx, generator, x.custom.unwrap()))
|
||||
})
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
let element_ty = element_val.iter().map(BasicValueEnum::get_type).collect_vec();
|
||||
|
@ -1084,7 +1064,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
// note that we would handle class methods directly in calls
|
||||
match generator.gen_expr(ctx, value)?.unwrap() {
|
||||
ValueEnum::Static(v) => v.get_field(*attr, ctx).map_or_else(|| {
|
||||
let v = v.to_basic_value_enum(ctx, generator)?;
|
||||
let v = v.to_basic_value_enum(ctx, generator, value.custom.unwrap())?;
|
||||
let index = ctx.get_attr_index(value.custom.unwrap(), *attr);
|
||||
Ok(ValueEnum::Dynamic(ctx.build_gep_and_load(
|
||||
v.into_pointer_value(),
|
||||
|
@ -1105,7 +1085,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
let left = generator
|
||||
.gen_expr(ctx, &values[0])?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)?
|
||||
.to_basic_value_enum(ctx, generator, values[0].custom.unwrap())?
|
||||
.into_int_value();
|
||||
let current = ctx.builder.get_insert_block().unwrap().get_parent().unwrap();
|
||||
let a_bb = ctx.ctx.append_basic_block(current, "a");
|
||||
|
@ -1121,7 +1101,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
let b = generator
|
||||
.gen_expr(ctx, &values[1])?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)?
|
||||
.to_basic_value_enum(ctx, generator, values[1].custom.unwrap())?
|
||||
.into_int_value();
|
||||
ctx.builder.build_unconditional_branch(cont_bb);
|
||||
(a, b)
|
||||
|
@ -1131,7 +1111,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
let a = generator
|
||||
.gen_expr(ctx, &values[1])?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)?
|
||||
.to_basic_value_enum(ctx, generator, values[1].custom.unwrap())?
|
||||
.into_int_value();
|
||||
ctx.builder.build_unconditional_branch(cont_bb);
|
||||
ctx.builder.position_at_end(b_bb);
|
||||
|
@ -1149,7 +1129,9 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
ExprKind::UnaryOp { op, operand } => {
|
||||
let ty = ctx.unifier.get_representative(operand.custom.unwrap());
|
||||
let val =
|
||||
generator.gen_expr(ctx, operand)?.unwrap().to_basic_value_enum(ctx, generator)?;
|
||||
generator.gen_expr(ctx, operand)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator, operand.custom.unwrap())?;
|
||||
if ty == ctx.primitives.bool {
|
||||
let val = val.into_int_value();
|
||||
match op {
|
||||
|
@ -1209,11 +1191,11 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
generator
|
||||
.gen_expr(ctx, lhs)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)?,
|
||||
.to_basic_value_enum(ctx, generator, lhs.custom.unwrap())?,
|
||||
generator
|
||||
.gen_expr(ctx, rhs)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)?,
|
||||
.to_basic_value_enum(ctx, generator, rhs.custom.unwrap())?,
|
||||
) {
|
||||
(lhs, rhs)
|
||||
} else {
|
||||
|
@ -1237,11 +1219,11 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
generator
|
||||
.gen_expr(ctx, lhs)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)?,
|
||||
.to_basic_value_enum(ctx, generator, lhs.custom.unwrap())?,
|
||||
generator
|
||||
.gen_expr(ctx, rhs)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)?,
|
||||
.to_basic_value_enum(ctx, generator, rhs.custom.unwrap())?,
|
||||
) {
|
||||
(lhs, rhs)
|
||||
} else {
|
||||
|
@ -1269,7 +1251,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
let test = generator
|
||||
.gen_expr(ctx, test)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)?
|
||||
.to_basic_value_enum(ctx, generator, test.custom.unwrap())?
|
||||
.into_int_value();
|
||||
let body_ty = body.custom.unwrap();
|
||||
let is_none = ctx.unifier.get_representative(body_ty) == ctx.primitives.none;
|
||||
|
@ -1289,7 +1271,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
match result {
|
||||
None => None,
|
||||
Some(v) => {
|
||||
let a = a.unwrap().to_basic_value_enum(ctx, generator)?;
|
||||
let a = a.unwrap().to_basic_value_enum(ctx, generator, body.custom.unwrap())?;
|
||||
Some(ctx.builder.build_store(v, a))
|
||||
}
|
||||
};
|
||||
|
@ -1299,7 +1281,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
match result {
|
||||
None => None,
|
||||
Some(v) => {
|
||||
let b = b.unwrap().to_basic_value_enum(ctx, generator)?;
|
||||
let b = b.unwrap().to_basic_value_enum(ctx, generator, orelse.custom.unwrap())?;
|
||||
Some(ctx.builder.build_store(v, b))
|
||||
}
|
||||
};
|
||||
|
@ -1372,23 +1354,66 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
}
|
||||
};
|
||||
// directly generate code for option.unwrap
|
||||
// since it needs location information from ast
|
||||
// since it needs to return static value to optimize for kernel invariant
|
||||
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")
|
||||
match val {
|
||||
ValueEnum::Static(v) => match v.get_field("_nac3_option".into(), ctx) {
|
||||
// if is none, raise exception directly
|
||||
None => {
|
||||
let err_msg = ctx.gen_string(generator, "");
|
||||
let current_fun = ctx
|
||||
.builder
|
||||
.get_insert_block()
|
||||
.unwrap()
|
||||
.get_parent()
|
||||
.unwrap();
|
||||
let unreachable_block = ctx.ctx.append_basic_block(
|
||||
current_fun,
|
||||
"unwrap_none_unreachable"
|
||||
);
|
||||
let exn_block = ctx.ctx.append_basic_block(
|
||||
current_fun,
|
||||
"unwrap_none_exception"
|
||||
);
|
||||
ctx.builder.build_unconditional_branch(exn_block);
|
||||
ctx.builder.position_at_end(exn_block);
|
||||
ctx.raise_exn(
|
||||
generator,
|
||||
"0:UnwrapNoneError",
|
||||
err_msg,
|
||||
[None, None, None],
|
||||
ctx.current_loc
|
||||
);
|
||||
ctx.builder.position_at_end(unreachable_block);
|
||||
let ptr = ctx
|
||||
.get_llvm_type(generator, value.custom.unwrap())
|
||||
.into_pointer_type()
|
||||
.const_null();
|
||||
return Ok(Some(ctx.builder.build_load(
|
||||
ptr,
|
||||
"unwrap_none_unreachable_load"
|
||||
).into()));
|
||||
}
|
||||
Some(v) => return Ok(Some(v)),
|
||||
}
|
||||
ValueEnum::Dynamic(BasicValueEnum::PointerValue(ptr)) => {
|
||||
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_load"
|
||||
).into()))
|
||||
}
|
||||
_ => unreachable!("option must be static or ptr")
|
||||
}
|
||||
}
|
||||
return Ok(generator
|
||||
|
@ -1408,7 +1433,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
let v = generator
|
||||
.gen_expr(ctx, value)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)?
|
||||
.to_basic_value_enum(ctx, generator, value.custom.unwrap())?
|
||||
.into_pointer_value();
|
||||
let ty = ctx.get_llvm_type(generator, *ty);
|
||||
let arr_ptr = ctx.build_gep_and_load(v, &[zero, zero]).into_pointer_value();
|
||||
|
@ -1417,6 +1442,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
let (start, end, step) =
|
||||
handle_slice_indices(lower, upper, step, ctx, generator, v)?;
|
||||
let length = calculate_len_for_slice_range(
|
||||
generator,
|
||||
ctx,
|
||||
start,
|
||||
ctx.builder
|
||||
|
@ -1438,8 +1464,8 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
let res_ind =
|
||||
handle_slice_indices(&None, &None, &None, ctx, generator, res_array_ret)?;
|
||||
list_slice_assignment(
|
||||
generator,
|
||||
ctx,
|
||||
generator.get_size_type(ctx.ctx),
|
||||
ty,
|
||||
res_array_ret,
|
||||
res_ind,
|
||||
|
@ -1454,7 +1480,7 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
let raw_index = generator
|
||||
.gen_expr(ctx, slice)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)?
|
||||
.to_basic_value_enum(ctx, generator, slice.custom.unwrap())?
|
||||
.into_int_value();
|
||||
let raw_index = ctx.builder.build_int_s_extend(
|
||||
raw_index,
|
||||
|
@ -1489,26 +1515,39 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
|
|||
[Some(raw_index), Some(len), None],
|
||||
expr.location,
|
||||
);
|
||||
ctx.build_gep_and_load(arr_ptr, &[index])
|
||||
ctx.build_gep_and_load(arr_ptr, &[index]).into()
|
||||
}
|
||||
} else if let TypeEnum::TTuple { .. } = &*ctx.unifier.get_ty(value.custom.unwrap()) {
|
||||
let v = generator
|
||||
.gen_expr(ctx, value)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)?
|
||||
.into_struct_value();
|
||||
let index: u32 =
|
||||
if let ExprKind::Constant { value: ast::Constant::Int(v), .. } = &slice.node {
|
||||
(*v).try_into().unwrap()
|
||||
} else {
|
||||
unreachable!("tuple subscript must be const int after type check");
|
||||
};
|
||||
ctx.builder.build_extract_value(v, index, "tup_elem").unwrap()
|
||||
let v = generator
|
||||
.gen_expr(ctx, value)?
|
||||
.unwrap();
|
||||
match v {
|
||||
ValueEnum::Dynamic(v) => {
|
||||
let v = v.into_struct_value();
|
||||
ctx.builder.build_extract_value(v, index, "tup_elem").unwrap().into()
|
||||
}
|
||||
ValueEnum::Static(v) => {
|
||||
match v.get_tuple_element(index) {
|
||||
Some(v) => v,
|
||||
None => {
|
||||
let tup = v
|
||||
.to_basic_value_enum(ctx, generator, value.custom.unwrap())?
|
||||
.into_struct_value();
|
||||
ctx.builder.build_extract_value(tup, index, "tup_elem").unwrap().into()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
unreachable!("should not be other subscriptable types after type check");
|
||||
}
|
||||
}
|
||||
.into(),
|
||||
},
|
||||
ExprKind::ListComp { .. } => gen_comprehension(generator, ctx, expr)?.into(),
|
||||
_ => unimplemented!(),
|
||||
}))
|
||||
|
|
|
@ -36,7 +36,7 @@ pub trait CodeGenerator {
|
|||
}
|
||||
|
||||
/// Generate object constructor and returns the constructed object.
|
||||
/// - signature: Function signature of the contructor.
|
||||
/// - signature: Function signature of the constructor.
|
||||
/// - def: Class definition for the constructor class.
|
||||
/// - params: Function parameters.
|
||||
fn gen_constructor<'ctx, 'a>(
|
||||
|
|
|
@ -6,7 +6,7 @@ use inkwell::{
|
|||
context::Context,
|
||||
memory_buffer::MemoryBuffer,
|
||||
module::Module,
|
||||
types::{BasicTypeEnum, IntType},
|
||||
types::BasicTypeEnum,
|
||||
values::{IntValue, PointerValue},
|
||||
AddressSpace, IntPredicate,
|
||||
};
|
||||
|
@ -34,6 +34,7 @@ pub fn load_irrt(ctx: &Context) -> Module {
|
|||
// repeated squaring method adapted from GNU Scientific Library:
|
||||
// https://git.savannah.gnu.org/cgit/gsl.git/tree/sys/pow_int.c
|
||||
pub fn integer_power<'ctx, 'a>(
|
||||
generator: &mut dyn CodeGenerator,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
base: IntValue<'ctx>,
|
||||
exp: IntValue<'ctx>,
|
||||
|
@ -51,7 +52,21 @@ pub fn integer_power<'ctx, 'a>(
|
|||
let fn_type = base_type.fn_type(&[base_type.into(), base_type.into()], false);
|
||||
ctx.module.add_function(symbol, fn_type, None)
|
||||
});
|
||||
// TODO: throw exception when exp < 0
|
||||
// throw exception when exp < 0
|
||||
let ge_zero = ctx.builder.build_int_compare(
|
||||
IntPredicate::SGE,
|
||||
exp,
|
||||
exp.get_type().const_zero(),
|
||||
"assert_int_pow_ge_0",
|
||||
);
|
||||
ctx.make_assert(
|
||||
generator,
|
||||
ge_zero,
|
||||
"0:ValueError",
|
||||
"integer power must be positive or zero",
|
||||
[None, None, None],
|
||||
ctx.current_loc,
|
||||
);
|
||||
ctx.builder
|
||||
.build_call(pow_fun, &[base.into(), exp.into()], "call_int_pow")
|
||||
.try_as_basic_value()
|
||||
|
@ -60,6 +75,7 @@ pub fn integer_power<'ctx, 'a>(
|
|||
}
|
||||
|
||||
pub fn calculate_len_for_slice_range<'ctx, 'a>(
|
||||
generator: &mut dyn CodeGenerator,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
start: IntValue<'ctx>,
|
||||
end: IntValue<'ctx>,
|
||||
|
@ -72,7 +88,21 @@ pub fn calculate_len_for_slice_range<'ctx, 'a>(
|
|||
ctx.module.add_function(SYMBOL, fn_t, None)
|
||||
});
|
||||
|
||||
// TODO: assert step != 0, throw exception if not
|
||||
// assert step != 0, throw exception if not
|
||||
let not_zero = ctx.builder.build_int_compare(
|
||||
IntPredicate::NE,
|
||||
step,
|
||||
step.get_type().const_zero(),
|
||||
"range_step_ne",
|
||||
);
|
||||
ctx.make_assert(
|
||||
generator,
|
||||
not_zero,
|
||||
"0:ValueError",
|
||||
"step must not be zero",
|
||||
[None, None, None],
|
||||
ctx.current_loc,
|
||||
);
|
||||
ctx.builder
|
||||
.build_call(len_func, &[start.into(), end.into(), step.into()], "calc_len")
|
||||
.try_as_basic_value()
|
||||
|
@ -129,7 +159,6 @@ pub fn handle_slice_indices<'a, 'ctx, G: CodeGenerator>(
|
|||
generator: &mut G,
|
||||
list: PointerValue<'ctx>,
|
||||
) -> Result<(IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>), String> {
|
||||
// TODO: throw exception when step is 0
|
||||
let int32 = ctx.ctx.i32_type();
|
||||
let zero = int32.const_zero();
|
||||
let one = int32.const_int(1, false);
|
||||
|
@ -154,8 +183,23 @@ pub fn handle_slice_indices<'a, 'ctx, G: CodeGenerator>(
|
|||
let step = generator
|
||||
.gen_expr(ctx, step)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)?
|
||||
.to_basic_value_enum(ctx, generator, ctx.primitives.int32)?
|
||||
.into_int_value();
|
||||
// assert step != 0, throw exception if not
|
||||
let not_zero = ctx.builder.build_int_compare(
|
||||
IntPredicate::NE,
|
||||
step,
|
||||
step.get_type().const_zero(),
|
||||
"range_step_ne",
|
||||
);
|
||||
ctx.make_assert(
|
||||
generator,
|
||||
not_zero,
|
||||
"0:ValueError",
|
||||
"slice step cannot be zero",
|
||||
[None, None, None],
|
||||
ctx.current_loc,
|
||||
);
|
||||
let len_id = ctx.builder.build_int_sub(length, one, "lenmin1");
|
||||
let neg = ctx.builder.build_int_compare(IntPredicate::SLT, step, zero, "step_is_neg");
|
||||
(
|
||||
|
@ -217,7 +261,7 @@ pub fn handle_slice_index_bound<'a, 'ctx, G: CodeGenerator>(
|
|||
ctx.module.add_function(SYMBOL, fn_t, None)
|
||||
});
|
||||
|
||||
let i = generator.gen_expr(ctx, i)?.unwrap().to_basic_value_enum(ctx, generator)?;
|
||||
let i = generator.gen_expr(ctx, i)?.unwrap().to_basic_value_enum(ctx, generator, i.custom.unwrap())?;
|
||||
Ok(ctx
|
||||
.builder
|
||||
.build_call(func, &[i.into(), length.into()], "bounded_ind")
|
||||
|
@ -231,14 +275,15 @@ pub fn handle_slice_index_bound<'a, 'ctx, G: CodeGenerator>(
|
|||
/// Order of tuples assign_idx and value_idx is ('start', 'end', 'step').
|
||||
/// Negative index should be handled before entering this function
|
||||
pub fn list_slice_assignment<'ctx, 'a>(
|
||||
generator: &mut dyn CodeGenerator,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
size_ty: IntType<'ctx>,
|
||||
ty: BasicTypeEnum<'ctx>,
|
||||
dest_arr: PointerValue<'ctx>,
|
||||
dest_idx: (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>),
|
||||
src_arr: PointerValue<'ctx>,
|
||||
src_idx: (IntValue<'ctx>, IntValue<'ctx>, IntValue<'ctx>),
|
||||
) {
|
||||
let size_ty = generator.get_size_type(ctx.ctx);
|
||||
let int8_ptr = ctx.ctx.i8_type().ptr_type(AddressSpace::Generic);
|
||||
let int32 = ctx.ctx.i32_type();
|
||||
let (fun_symbol, elem_ptr_type) = ("__nac3_list_slice_assign_var_size", int8_ptr);
|
||||
|
@ -282,8 +327,67 @@ pub fn list_slice_assignment<'ctx, 'a>(
|
|||
let src_len = ctx.builder.build_int_truncate_or_bit_cast(src_len, int32, "srclen32");
|
||||
|
||||
// index in bound and positive should be done
|
||||
// TODO: assert if dest.step == 1 then len(src) <= len(dest) else len(src) == len(dest), and
|
||||
// assert if dest.step == 1 then len(src) <= len(dest) else len(src) == len(dest), and
|
||||
// throw exception if not satisfied
|
||||
let src_end = ctx.builder
|
||||
.build_select(
|
||||
ctx.builder.build_int_compare(
|
||||
inkwell::IntPredicate::SLT,
|
||||
src_idx.2,
|
||||
zero,
|
||||
"is_neg",
|
||||
),
|
||||
ctx.builder.build_int_sub(src_idx.1, one, "e_min_one"),
|
||||
ctx.builder.build_int_add(src_idx.1, one, "e_add_one"),
|
||||
"final_e",
|
||||
)
|
||||
.into_int_value();
|
||||
let dest_end = ctx.builder
|
||||
.build_select(
|
||||
ctx.builder.build_int_compare(
|
||||
inkwell::IntPredicate::SLT,
|
||||
dest_idx.2,
|
||||
zero,
|
||||
"is_neg",
|
||||
),
|
||||
ctx.builder.build_int_sub(dest_idx.1, one, "e_min_one"),
|
||||
ctx.builder.build_int_add(dest_idx.1, one, "e_add_one"),
|
||||
"final_e",
|
||||
)
|
||||
.into_int_value();
|
||||
let src_slice_len =
|
||||
calculate_len_for_slice_range(generator, ctx, src_idx.0, src_end, src_idx.2);
|
||||
let dest_slice_len =
|
||||
calculate_len_for_slice_range(generator, ctx, dest_idx.0, dest_end, dest_idx.2);
|
||||
let src_eq_dest = ctx.builder.build_int_compare(
|
||||
IntPredicate::EQ,
|
||||
src_slice_len,
|
||||
dest_slice_len,
|
||||
"slice_src_eq_dest",
|
||||
);
|
||||
let src_slt_dest = ctx.builder.build_int_compare(
|
||||
IntPredicate::SLT,
|
||||
src_slice_len,
|
||||
dest_slice_len,
|
||||
"slice_src_slt_dest",
|
||||
);
|
||||
let dest_step_eq_one = ctx.builder.build_int_compare(
|
||||
IntPredicate::EQ,
|
||||
dest_idx.2,
|
||||
dest_idx.2.get_type().const_int(1, false),
|
||||
"slice_dest_step_eq_one",
|
||||
);
|
||||
let cond_1 = ctx.builder.build_and(dest_step_eq_one, src_slt_dest, "slice_cond_1");
|
||||
let cond = ctx.builder.build_or(src_eq_dest, cond_1, "slice_cond");
|
||||
ctx.make_assert(
|
||||
generator,
|
||||
cond,
|
||||
"0:ValueError",
|
||||
"attempt to assign sequence of size {0} to slice of size {1} with step size {2}",
|
||||
[Some(src_slice_len), Some(dest_slice_len), Some(dest_idx.2)],
|
||||
ctx.current_loc,
|
||||
);
|
||||
|
||||
let new_len = {
|
||||
let args = vec![
|
||||
dest_idx.0.into(), // dest start idx
|
||||
|
|
|
@ -20,7 +20,7 @@ use inkwell::{
|
|||
values::{BasicValueEnum, FunctionValue, PhiValue, PointerValue}
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use nac3parser::ast::{Stmt, StrRef};
|
||||
use nac3parser::ast::{Stmt, StrRef, Location};
|
||||
use parking_lot::{Condvar, Mutex};
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::sync::{
|
||||
|
@ -77,6 +77,7 @@ pub struct CodeGenContext<'ctx, 'a> {
|
|||
pub outer_catch_clauses:
|
||||
Option<(Vec<Option<BasicValueEnum<'ctx>>>, BasicBlock<'ctx>, PhiValue<'ctx>)>,
|
||||
pub need_sret: bool,
|
||||
pub current_loc: Location,
|
||||
}
|
||||
|
||||
impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
|
||||
|
@ -570,7 +571,8 @@ pub fn gen_func_impl<'ctx, G: CodeGenerator, F: FnOnce(&mut G, &mut CodeGenConte
|
|||
module,
|
||||
unifier,
|
||||
static_value_store,
|
||||
need_sret: has_sret
|
||||
need_sret: has_sret,
|
||||
current_loc: Default::default(),
|
||||
};
|
||||
|
||||
let result = codegen_function(generator, &mut code_gen_context);
|
||||
|
|
|
@ -54,7 +54,11 @@ pub fn gen_store_target<'ctx, 'a, G: CodeGenerator>(
|
|||
}
|
||||
ExprKind::Attribute { value, attr, .. } => {
|
||||
let index = ctx.get_attr_index(value.custom.unwrap(), *attr);
|
||||
let val = generator.gen_expr(ctx, value)?.unwrap().to_basic_value_enum(ctx, generator)?;
|
||||
let val = generator.gen_expr(ctx, value)?.unwrap().to_basic_value_enum(
|
||||
ctx,
|
||||
generator,
|
||||
value.custom.unwrap(),
|
||||
)?;
|
||||
let ptr = if let BasicValueEnum::PointerValue(v) = val {
|
||||
v
|
||||
} else {
|
||||
|
@ -72,17 +76,58 @@ pub fn gen_store_target<'ctx, 'a, G: CodeGenerator>(
|
|||
}
|
||||
}
|
||||
ExprKind::Subscript { value, slice, .. } => {
|
||||
assert!(matches!(
|
||||
ctx.unifier.get_ty_immutable(value.custom.unwrap()).as_ref(),
|
||||
TypeEnum::TList { .. },
|
||||
));
|
||||
let i32_type = ctx.ctx.i32_type();
|
||||
let zero = i32_type.const_zero();
|
||||
let v = generator
|
||||
.gen_expr(ctx, value)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)?
|
||||
.to_basic_value_enum(ctx, generator, value.custom.unwrap())?
|
||||
.into_pointer_value();
|
||||
let index = generator
|
||||
let len = ctx
|
||||
.build_gep_and_load(v, &[zero, i32_type.const_int(1, false)])
|
||||
.into_int_value();
|
||||
let raw_index = generator
|
||||
.gen_expr(ctx, slice)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)?
|
||||
.to_basic_value_enum(ctx, generator, slice.custom.unwrap())?
|
||||
.into_int_value();
|
||||
let raw_index = ctx.builder.build_int_s_extend(
|
||||
raw_index,
|
||||
generator.get_size_type(ctx.ctx),
|
||||
"sext",
|
||||
);
|
||||
// handle negative index
|
||||
let is_negative = ctx.builder.build_int_compare(
|
||||
inkwell::IntPredicate::SLT,
|
||||
raw_index,
|
||||
generator.get_size_type(ctx.ctx).const_zero(),
|
||||
"is_neg",
|
||||
);
|
||||
let adjusted = ctx.builder.build_int_add(raw_index, len, "adjusted");
|
||||
let index = ctx
|
||||
.builder
|
||||
.build_select(is_negative, adjusted, raw_index, "index")
|
||||
.into_int_value();
|
||||
// unsigned less than is enough, because negative index after adjustment is
|
||||
// bigger than the length (for unsigned cmp)
|
||||
let bound_check = ctx.builder.build_int_compare(
|
||||
inkwell::IntPredicate::ULT,
|
||||
index,
|
||||
len,
|
||||
"inbound",
|
||||
);
|
||||
ctx.make_assert(
|
||||
generator,
|
||||
bound_check,
|
||||
"0:IndexError",
|
||||
"index {0} out of bounds 0:{1}",
|
||||
[Some(raw_index), Some(len), None],
|
||||
slice.location,
|
||||
);
|
||||
unsafe {
|
||||
let arr_ptr = ctx
|
||||
.build_gep_and_load(v, &[i32_type.const_zero(), i32_type.const_zero()])
|
||||
|
@ -102,7 +147,9 @@ pub fn gen_assign<'ctx, 'a, G: CodeGenerator>(
|
|||
) -> Result<(), String> {
|
||||
match &target.node {
|
||||
ExprKind::Tuple { elts, .. } => {
|
||||
if let BasicValueEnum::StructValue(v) = value.to_basic_value_enum(ctx, generator)? {
|
||||
if let BasicValueEnum::StructValue(v) =
|
||||
value.to_basic_value_enum(ctx, generator, target.custom.unwrap())?
|
||||
{
|
||||
for (i, elt) in elts.iter().enumerate() {
|
||||
let v = ctx
|
||||
.builder
|
||||
|
@ -121,11 +168,13 @@ pub fn gen_assign<'ctx, 'a, G: CodeGenerator>(
|
|||
let ls = generator
|
||||
.gen_expr(ctx, ls)?
|
||||
.unwrap()
|
||||
.to_basic_value_enum(ctx, generator)?
|
||||
.to_basic_value_enum(ctx, generator, ls.custom.unwrap())?
|
||||
.into_pointer_value();
|
||||
let (start, end, step) =
|
||||
handle_slice_indices(lower, upper, step, ctx, generator, ls)?;
|
||||
let value = value.to_basic_value_enum(ctx, generator)?.into_pointer_value();
|
||||
let value = value
|
||||
.to_basic_value_enum(ctx, generator, target.custom.unwrap())?
|
||||
.into_pointer_value();
|
||||
let ty =
|
||||
if let TypeEnum::TList { ty } = &*ctx.unifier.get_ty(target.custom.unwrap()) {
|
||||
ctx.get_llvm_type(generator, *ty)
|
||||
|
@ -133,15 +182,7 @@ pub fn gen_assign<'ctx, 'a, G: CodeGenerator>(
|
|||
unreachable!()
|
||||
};
|
||||
let src_ind = handle_slice_indices(&None, &None, &None, ctx, generator, value)?;
|
||||
list_slice_assignment(
|
||||
ctx,
|
||||
generator.get_size_type(ctx.ctx),
|
||||
ty,
|
||||
ls,
|
||||
(start, end, step),
|
||||
value,
|
||||
src_ind,
|
||||
)
|
||||
list_slice_assignment(generator, ctx, ty, ls, (start, end, step), value, src_ind)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
|
@ -155,7 +196,7 @@ pub fn gen_assign<'ctx, 'a, G: CodeGenerator>(
|
|||
*static_value = Some(s.clone());
|
||||
}
|
||||
}
|
||||
let val = value.to_basic_value_enum(ctx, generator)?;
|
||||
let val = value.to_basic_value_enum(ctx, generator, target.custom.unwrap())?;
|
||||
ctx.builder.build_store(ptr, val);
|
||||
}
|
||||
};
|
||||
|
@ -185,7 +226,11 @@ pub fn gen_for<'ctx, 'a, G: CodeGenerator>(
|
|||
// store loop bb information and restore it later
|
||||
let loop_bb = ctx.loop_target.replace((test_bb, cont_bb));
|
||||
|
||||
let iter_val = generator.gen_expr(ctx, iter)?.unwrap().to_basic_value_enum(ctx, generator)?;
|
||||
let iter_val = generator.gen_expr(ctx, iter)?.unwrap().to_basic_value_enum(
|
||||
ctx,
|
||||
generator,
|
||||
iter.custom.unwrap(),
|
||||
)?;
|
||||
if ctx.unifier.unioned(iter.custom.unwrap(), ctx.primitives.range) {
|
||||
// setup
|
||||
let iter_val = iter_val.into_pointer_value();
|
||||
|
@ -298,7 +343,11 @@ pub fn gen_while<'ctx, 'a, G: CodeGenerator>(
|
|||
let loop_bb = ctx.loop_target.replace((test_bb, cont_bb));
|
||||
ctx.builder.build_unconditional_branch(test_bb);
|
||||
ctx.builder.position_at_end(test_bb);
|
||||
let test = generator.gen_expr(ctx, test)?.unwrap().to_basic_value_enum(ctx, generator)?;
|
||||
let test = generator.gen_expr(ctx, test)?.unwrap().to_basic_value_enum(
|
||||
ctx,
|
||||
generator,
|
||||
test.custom.unwrap(),
|
||||
)?;
|
||||
if let BasicValueEnum::IntValue(test) = test {
|
||||
ctx.builder.build_conditional_branch(test, body_bb, orelse_bb);
|
||||
} else {
|
||||
|
@ -359,7 +408,11 @@ pub fn gen_if<'ctx, 'a, G: CodeGenerator>(
|
|||
};
|
||||
ctx.builder.build_unconditional_branch(test_bb);
|
||||
ctx.builder.position_at_end(test_bb);
|
||||
let test = generator.gen_expr(ctx, test)?.unwrap().to_basic_value_enum(ctx, generator)?;
|
||||
let test = generator.gen_expr(ctx, test)?.unwrap().to_basic_value_enum(
|
||||
ctx,
|
||||
generator,
|
||||
test.custom.unwrap(),
|
||||
)?;
|
||||
if let BasicValueEnum::IntValue(test) = test {
|
||||
ctx.builder.build_conditional_branch(test, body_bb, orelse_bb);
|
||||
} else {
|
||||
|
@ -422,8 +475,8 @@ pub fn final_proxy<'ctx, 'a>(
|
|||
final_paths.push(block);
|
||||
}
|
||||
|
||||
pub fn get_builtins<'ctx, 'a, G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
pub fn get_builtins<'ctx, 'a>(
|
||||
generator: &mut dyn CodeGenerator,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
symbol: &str,
|
||||
) -> FunctionValue<'ctx> {
|
||||
|
@ -456,7 +509,7 @@ pub fn exn_constructor<'ctx, 'a>(
|
|||
generator: &mut dyn CodeGenerator,
|
||||
) -> Result<Option<BasicValueEnum<'ctx>>, String> {
|
||||
let (zelf_ty, zelf) = obj.unwrap();
|
||||
let zelf = zelf.to_basic_value_enum(ctx, generator)?.into_pointer_value();
|
||||
let zelf = zelf.to_basic_value_enum(ctx, generator, zelf_ty)?.into_pointer_value();
|
||||
let int32 = ctx.ctx.i32_type();
|
||||
let zero = int32.const_zero();
|
||||
let zelf_id = {
|
||||
|
@ -479,14 +532,14 @@ pub fn exn_constructor<'ctx, 'a>(
|
|||
let ptr =
|
||||
ctx.builder.build_in_bounds_gep(zelf, &[zero, int32.const_int(5, false)], "exn.msg");
|
||||
let msg = if !args.is_empty() {
|
||||
args.remove(0).1.to_basic_value_enum(ctx, generator)?
|
||||
args.remove(0).1.to_basic_value_enum(ctx, generator, ctx.primitives.str)?
|
||||
} else {
|
||||
empty_string
|
||||
};
|
||||
ctx.builder.build_store(ptr, msg);
|
||||
for i in [6, 7, 8].iter() {
|
||||
let value = if !args.is_empty() {
|
||||
args.remove(0).1.to_basic_value_enum(ctx, generator)?
|
||||
args.remove(0).1.to_basic_value_enum(ctx, generator, ctx.primitives.int64)?
|
||||
} else {
|
||||
ctx.ctx.i64_type().const_zero().into()
|
||||
};
|
||||
|
@ -519,8 +572,8 @@ pub fn exn_constructor<'ctx, 'a>(
|
|||
Ok(Some(zelf.into()))
|
||||
}
|
||||
|
||||
pub fn gen_raise<'ctx, 'a, G: CodeGenerator>(
|
||||
generator: &mut G,
|
||||
pub fn gen_raise<'ctx, 'a>(
|
||||
generator: &mut dyn CodeGenerator,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
exception: Option<&BasicValueEnum<'ctx>>,
|
||||
loc: Location,
|
||||
|
@ -908,7 +961,11 @@ pub fn gen_return<'ctx, 'a, G: CodeGenerator>(
|
|||
) -> Result<(), String> {
|
||||
let value = value
|
||||
.as_ref()
|
||||
.map(|v| generator.gen_expr(ctx, v).and_then(|v| v.unwrap().to_basic_value_enum(ctx, generator)))
|
||||
.map(|v_expr| {
|
||||
generator.gen_expr(ctx, v_expr).and_then(|v| {
|
||||
v.unwrap().to_basic_value_enum(ctx, generator, v_expr.custom.unwrap())
|
||||
})
|
||||
})
|
||||
.transpose()?;
|
||||
if let Some(return_target) = ctx.return_target {
|
||||
if let Some(value) = value {
|
||||
|
@ -931,6 +988,7 @@ pub fn gen_stmt<'ctx, 'a, G: CodeGenerator>(
|
|||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
stmt: &Stmt<Option<Type>>,
|
||||
) -> Result<(), String> {
|
||||
ctx.current_loc = stmt.location;
|
||||
match &stmt.node {
|
||||
StmtKind::Pass { .. } => {}
|
||||
StmtKind::Expr { value, .. } => {
|
||||
|
@ -968,20 +1026,28 @@ pub fn gen_stmt<'ctx, 'a, G: CodeGenerator>(
|
|||
StmtKind::Try { .. } => gen_try(generator, ctx, stmt)?,
|
||||
StmtKind::Raise { exc, .. } => {
|
||||
if let Some(exc) = exc {
|
||||
let exc =
|
||||
generator.gen_expr(ctx, exc)?.unwrap().to_basic_value_enum(ctx, generator)?;
|
||||
let exc = generator.gen_expr(ctx, exc)?.unwrap().to_basic_value_enum(
|
||||
ctx,
|
||||
generator,
|
||||
exc.custom.unwrap(),
|
||||
)?;
|
||||
gen_raise(generator, ctx, Some(&exc), stmt.location);
|
||||
} else {
|
||||
gen_raise(generator, ctx, None, stmt.location);
|
||||
}
|
||||
}
|
||||
StmtKind::Assert { test, msg, .. } => {
|
||||
let test =
|
||||
generator.gen_expr(ctx, test)?.unwrap().to_basic_value_enum(ctx, generator)?;
|
||||
let test = generator.gen_expr(ctx, test)?.unwrap().to_basic_value_enum(
|
||||
ctx,
|
||||
generator,
|
||||
test.custom.unwrap(),
|
||||
)?;
|
||||
let err_msg = match msg {
|
||||
Some(msg) => {
|
||||
generator.gen_expr(ctx, msg)?.unwrap().to_basic_value_enum(ctx, generator)?
|
||||
}
|
||||
Some(msg) => generator.gen_expr(ctx, msg)?.unwrap().to_basic_value_enum(
|
||||
ctx,
|
||||
generator,
|
||||
msg.custom.unwrap(),
|
||||
)?,
|
||||
None => ctx.gen_string(generator, ""),
|
||||
};
|
||||
ctx.make_assert_impl(
|
||||
|
|
|
@ -71,6 +71,7 @@ pub trait StaticValue {
|
|||
&self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
expected_ty: Type,
|
||||
) -> Result<BasicValueEnum<'ctx>, String>;
|
||||
|
||||
fn get_field<'ctx, 'a>(
|
||||
|
@ -78,6 +79,8 @@ pub trait StaticValue {
|
|||
name: StrRef,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
) -> Option<ValueEnum<'ctx>>;
|
||||
|
||||
fn get_tuple_element<'ctx>(&self, index: u32) -> Option<ValueEnum<'ctx>>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
|
@ -121,9 +124,10 @@ impl<'ctx> ValueEnum<'ctx> {
|
|||
self,
|
||||
ctx: &mut CodeGenContext<'ctx, 'a>,
|
||||
generator: &mut dyn CodeGenerator,
|
||||
expected_ty: Type,
|
||||
) -> Result<BasicValueEnum<'ctx>, String> {
|
||||
match self {
|
||||
ValueEnum::Static(v) => v.to_basic_value_enum(ctx, generator),
|
||||
ValueEnum::Static(v) => v.to_basic_value_enum(ctx, generator, expected_ty),
|
||||
ValueEnum::Dynamic(v) => Ok(v),
|
||||
}
|
||||
}
|
||||
|
@ -363,7 +367,7 @@ impl dyn SymbolResolver + Send + Sync {
|
|||
unreachable!("expected class definition")
|
||||
}
|
||||
},
|
||||
&mut |id| format!("var{}", id),
|
||||
&mut |id| format!("typevar{}", id),
|
||||
&mut None,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -224,7 +224,12 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
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)?;
|
||||
let expect_ty = obj.clone().unwrap().0;
|
||||
let obj_val = obj.unwrap().1.clone().to_basic_value_enum(
|
||||
ctx,
|
||||
generator,
|
||||
expect_ty,
|
||||
)?;
|
||||
if let BasicValueEnum::PointerValue(ptr) = obj_val {
|
||||
Ok(Some(ctx.builder.build_is_not_null(ptr, "is_some").into()))
|
||||
} else {
|
||||
|
@ -244,7 +249,12 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
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)?;
|
||||
let expect_ty = obj.clone().unwrap().0;
|
||||
let obj_val = obj.unwrap().1.clone().to_basic_value_enum(
|
||||
ctx,
|
||||
generator,
|
||||
expect_ty,
|
||||
)?;
|
||||
if let BasicValueEnum::PointerValue(ptr) = obj_val {
|
||||
Ok(Some(ctx.builder.build_is_null(ptr, "is_none").into()))
|
||||
} else {
|
||||
|
@ -290,7 +300,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
let float = ctx.primitives.float;
|
||||
let boolean = ctx.primitives.bool;
|
||||
let arg_ty = fun.0.args[0].ty;
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
|
||||
Ok(if ctx.unifier.unioned(arg_ty, boolean) {
|
||||
Some(
|
||||
ctx.builder
|
||||
|
@ -355,7 +365,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
let float = ctx.primitives.float;
|
||||
let boolean = ctx.primitives.bool;
|
||||
let arg_ty = fun.0.args[0].ty;
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
|
||||
Ok(
|
||||
if ctx.unifier.unioned(arg_ty, boolean)
|
||||
|| ctx.unifier.unioned(arg_ty, uint32)
|
||||
|
@ -422,7 +432,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
let float = ctx.primitives.float;
|
||||
let boolean = ctx.primitives.bool;
|
||||
let arg_ty = fun.0.args[0].ty;
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
|
||||
let res = if ctx.unifier.unioned(arg_ty, boolean) {
|
||||
ctx.builder
|
||||
.build_int_z_extend(arg.into_int_value(), ctx.ctx.i64_type(), "zext")
|
||||
|
@ -474,7 +484,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
let float = ctx.primitives.float;
|
||||
let boolean = ctx.primitives.bool;
|
||||
let arg_ty = fun.0.args[0].ty;
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
|
||||
let res = if ctx.unifier.unioned(arg_ty, int32)
|
||||
|| ctx.unifier.unioned(arg_ty, uint32)
|
||||
|| ctx.unifier.unioned(arg_ty, boolean)
|
||||
|
@ -521,7 +531,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
let boolean = ctx.primitives.bool;
|
||||
let float = ctx.primitives.float;
|
||||
let arg_ty = fun.0.args[0].ty;
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
|
||||
Ok(
|
||||
if ctx.unifier.unioned(arg_ty, boolean)
|
||||
|| ctx.unifier.unioned(arg_ty, int32)
|
||||
|
@ -557,7 +567,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||
|ctx, _, _, args, generator| {
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
|
||||
let round_intrinsic =
|
||||
ctx.module.get_function("llvm.round.f64").unwrap_or_else(|| {
|
||||
let float = ctx.ctx.f64_type();
|
||||
|
@ -597,7 +607,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||
|ctx, _, _, args, generator| {
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
|
||||
let round_intrinsic =
|
||||
ctx.module.get_function("llvm.round.f64").unwrap_or_else(|| {
|
||||
let float = ctx.ctx.f64_type();
|
||||
|
@ -655,23 +665,44 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
let mut step = None;
|
||||
let int32 = ctx.ctx.i32_type();
|
||||
let zero = int32.const_zero();
|
||||
let ty_i32 = ctx.primitives.int32;
|
||||
for (i, arg) in args.iter().enumerate() {
|
||||
if arg.0 == Some("start".into()) {
|
||||
start = Some(arg.1.clone().to_basic_value_enum(ctx, generator)?);
|
||||
start = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
|
||||
} else if arg.0 == Some("stop".into()) {
|
||||
stop = Some(arg.1.clone().to_basic_value_enum(ctx, generator)?);
|
||||
stop = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
|
||||
} else if arg.0 == Some("step".into()) {
|
||||
step = Some(arg.1.clone().to_basic_value_enum(ctx, generator)?);
|
||||
step = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
|
||||
} else if i == 0 {
|
||||
start = Some(arg.1.clone().to_basic_value_enum(ctx, generator)?);
|
||||
start = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
|
||||
} else if i == 1 {
|
||||
stop = Some(arg.1.clone().to_basic_value_enum(ctx, generator)?);
|
||||
stop = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
|
||||
} else if i == 2 {
|
||||
step = Some(arg.1.clone().to_basic_value_enum(ctx, generator)?);
|
||||
step = Some(arg.1.clone().to_basic_value_enum(ctx, generator, ty_i32)?);
|
||||
}
|
||||
}
|
||||
// TODO: error when step == 0
|
||||
let step = step.unwrap_or_else(|| int32.const_int(1, false).into());
|
||||
let step = match step {
|
||||
Some(step) => {
|
||||
let step = step.into_int_value();
|
||||
// assert step != 0, throw exception if not
|
||||
let not_zero = ctx.builder.build_int_compare(
|
||||
IntPredicate::NE,
|
||||
step,
|
||||
step.get_type().const_zero(),
|
||||
"range_step_ne",
|
||||
);
|
||||
ctx.make_assert(
|
||||
generator,
|
||||
not_zero,
|
||||
"0:ValueError",
|
||||
"range() step must not be zero",
|
||||
[None, None, None],
|
||||
ctx.current_loc,
|
||||
);
|
||||
step
|
||||
}
|
||||
None => int32.const_int(1, false),
|
||||
};
|
||||
let stop = stop.unwrap_or_else(|| {
|
||||
let v = start.unwrap();
|
||||
start = None;
|
||||
|
@ -714,8 +745,9 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
instance_to_stmt: Default::default(),
|
||||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||
|ctx, _, _, args, generator| {
|
||||
Ok(Some(args[0].1.clone().to_basic_value_enum(ctx, generator)?))
|
||||
|ctx, _, fun, args, generator| {
|
||||
let arg_ty = fun.0.args[0].ty;
|
||||
Ok(Some(args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?))
|
||||
},
|
||||
)))),
|
||||
loc: None,
|
||||
|
@ -739,7 +771,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
let float = ctx.primitives.float;
|
||||
let boolean = ctx.primitives.bool;
|
||||
let arg_ty = fun.0.args[0].ty;
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
|
||||
Ok(if ctx.unifier.unioned(arg_ty, boolean) {
|
||||
Some(arg)
|
||||
} else if ctx.unifier.unioned(arg_ty, int32) {
|
||||
|
@ -797,7 +829,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||
|ctx, _, _, args, generator| {
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
|
||||
let floor_intrinsic =
|
||||
ctx.module.get_function("llvm.floor.f64").unwrap_or_else(|| {
|
||||
let float = ctx.ctx.f64_type();
|
||||
|
@ -837,7 +869,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||
|ctx, _, _, args, generator| {
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
|
||||
let floor_intrinsic =
|
||||
ctx.module.get_function("llvm.floor.f64").unwrap_or_else(|| {
|
||||
let float = ctx.ctx.f64_type();
|
||||
|
@ -877,7 +909,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||
|ctx, _, _, args, generator| {
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
|
||||
let ceil_intrinsic =
|
||||
ctx.module.get_function("llvm.ceil.f64").unwrap_or_else(|| {
|
||||
let float = ctx.ctx.f64_type();
|
||||
|
@ -917,7 +949,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
resolver: None,
|
||||
codegen_callback: Some(Arc::new(GenCall::new(Box::new(
|
||||
|ctx, _, _, args, generator| {
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, ctx.primitives.float)?;
|
||||
let ceil_intrinsic =
|
||||
ctx.module.get_function("llvm.ceil.f64").unwrap_or_else(|| {
|
||||
let float = ctx.ctx.f64_type();
|
||||
|
@ -969,11 +1001,11 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
|ctx, _, fun, args, generator| {
|
||||
let range_ty = ctx.primitives.range;
|
||||
let arg_ty = fun.0.args[0].ty;
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
|
||||
let arg = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
|
||||
Ok(if ctx.unifier.unioned(arg_ty, range_ty) {
|
||||
let arg = arg.into_pointer_value();
|
||||
let (start, end, step) = destructure_range(ctx, arg);
|
||||
Some(calculate_len_for_slice_range(ctx, start, end, step).into())
|
||||
Some(calculate_len_for_slice_range(generator, ctx, start, end, step).into())
|
||||
} else {
|
||||
let int32 = ctx.ctx.i32_type();
|
||||
let zero = int32.const_zero();
|
||||
|
@ -1023,8 +1055,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
let llvm_f64 = ctx.ctx.f64_type().as_basic_type_enum();
|
||||
let m_ty = fun.0.args[0].ty;
|
||||
let n_ty = fun.0.args[1].ty;
|
||||
let m_val = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
|
||||
let n_val = args[1].1.clone().to_basic_value_enum(ctx, generator)?;
|
||||
let m_val = args[0].1.clone().to_basic_value_enum(ctx, generator, m_ty)?;
|
||||
let n_val = args[1].1.clone().to_basic_value_enum(ctx, generator, n_ty)?;
|
||||
let mut is_type = |a: Type, b: Type| ctx.unifier.unioned(a, b);
|
||||
let (fun_name, arg_ty) = if is_type(m_ty, n_ty) && is_type(n_ty, boolean) {
|
||||
("llvm.umin.i1", llvm_i1)
|
||||
|
@ -1085,8 +1117,8 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
let llvm_f64 = ctx.ctx.f64_type().as_basic_type_enum();
|
||||
let m_ty = fun.0.args[0].ty;
|
||||
let n_ty = fun.0.args[1].ty;
|
||||
let m_val = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
|
||||
let n_val = args[1].1.clone().to_basic_value_enum(ctx, generator)?;
|
||||
let m_val = args[0].1.clone().to_basic_value_enum(ctx, generator, m_ty)?;
|
||||
let n_val = args[1].1.clone().to_basic_value_enum(ctx, generator, n_ty)?;
|
||||
let mut is_type = |a: Type, b: Type| ctx.unifier.unioned(a, b);
|
||||
let (fun_name, arg_ty) = if is_type(m_ty, n_ty) && is_type(n_ty, boolean) {
|
||||
("llvm.umax.i1", llvm_i1)
|
||||
|
@ -1143,7 +1175,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
let llvm_i64 = ctx.ctx.i64_type().as_basic_type_enum();
|
||||
let llvm_f64 = ctx.ctx.f64_type().as_basic_type_enum();
|
||||
let n_ty = fun.0.args[0].ty;
|
||||
let n_val = args[0].1.clone().to_basic_value_enum(ctx, generator)?;
|
||||
let n_val = args[0].1.clone().to_basic_value_enum(ctx, generator, n_ty)?;
|
||||
let mut is_type = |a: Type, b: Type| ctx.unifier.unioned(a, b);
|
||||
let mut is_float = false;
|
||||
let (fun_name, arg_ty) =
|
||||
|
@ -1200,8 +1232,9 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo {
|
|||
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)?;
|
||||
|ctx, _, fun, args, generator| {
|
||||
let arg_ty = fun.0.args[0].ty;
|
||||
let arg_val = args[0].1.clone().to_basic_value_enum(ctx, generator, arg_ty)?;
|
||||
let alloca = ctx.builder.build_alloca(arg_val.get_type(), "alloca_some");
|
||||
ctx.builder.build_store(alloca, arg_val);
|
||||
Ok(Some(alloca.into()))
|
||||
|
|
|
@ -4,7 +4,7 @@ use std::rc::Rc;
|
|||
use crate::{
|
||||
codegen::{expr::get_subst_key, stmt::exn_constructor},
|
||||
symbol_resolver::SymbolValue,
|
||||
typecheck::{type_inferencer::{FunctionData, Inferencer}, escape_analysis::EscapeAnalyzer},
|
||||
typecheck::type_inferencer::{FunctionData, Inferencer},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
@ -434,11 +434,11 @@ impl TopLevelComposer {
|
|||
|
||||
// check if all are unique type vars
|
||||
let all_unique_type_var = {
|
||||
let mut occured_type_var_id: HashSet<u32> = HashSet::new();
|
||||
let mut occurred_type_var_id: HashSet<u32> = HashSet::new();
|
||||
type_vars.iter().all(|x| {
|
||||
let ty = unifier.get_ty(*x);
|
||||
if let TypeEnum::TVar { id, .. } = ty.as_ref() {
|
||||
occured_type_var_id.insert(*id)
|
||||
occurred_type_var_id.insert(*id)
|
||||
} else {
|
||||
false
|
||||
}
|
||||
|
@ -536,7 +536,7 @@ impl TopLevelComposer {
|
|||
}
|
||||
has_base = true;
|
||||
|
||||
// the function parse_ast_to make sure that no type var occured in
|
||||
// the function parse_ast_to make sure that no type var occurred in
|
||||
// bast_ty if it is a CustomClassKind
|
||||
let base_ty = parse_ast_to_type_annotation_kinds(
|
||||
class_resolver,
|
||||
|
@ -696,7 +696,7 @@ impl TopLevelComposer {
|
|||
return Err(errors.into_iter().sorted().join("\n----------\n"));
|
||||
}
|
||||
|
||||
// handle the inheritanced methods and fields
|
||||
// handle the inherited methods and fields
|
||||
// Note: we cannot defer error handling til the end of the loop, because there is loop
|
||||
// carried dependency, ignoring the error (temporarily) will cause all assumptions to break
|
||||
// and produce weird error messages
|
||||
|
@ -825,9 +825,9 @@ impl TopLevelComposer {
|
|||
let mut function_var_map: HashMap<u32, Type> = HashMap::new();
|
||||
let arg_types = {
|
||||
// make sure no duplicate parameter
|
||||
let mut defined_paramter_name: HashSet<_> = HashSet::new();
|
||||
let mut defined_parameter_name: HashSet<_> = HashSet::new();
|
||||
for x in args.args.iter() {
|
||||
if !defined_paramter_name.insert(x.node.arg)
|
||||
if !defined_parameter_name.insert(x.node.arg)
|
||||
|| keyword_list.contains(&x.node.arg)
|
||||
{
|
||||
return Err(format!(
|
||||
|
@ -1074,10 +1074,10 @@ impl TopLevelComposer {
|
|||
|
||||
let arg_types: Vec<FuncArg> = {
|
||||
// check method parameters cannot have same name
|
||||
let mut defined_paramter_name: HashSet<_> = HashSet::new();
|
||||
let mut defined_parameter_name: HashSet<_> = HashSet::new();
|
||||
let zelf: StrRef = "self".into();
|
||||
for x in args.args.iter() {
|
||||
if !defined_paramter_name.insert(x.node.arg)
|
||||
if !defined_parameter_name.insert(x.node.arg)
|
||||
|| (keyword_list.contains(&x.node.arg) && x.node.arg != zelf)
|
||||
{
|
||||
return Err(format!(
|
||||
|
@ -1088,13 +1088,13 @@ impl TopLevelComposer {
|
|||
}
|
||||
}
|
||||
|
||||
if name == &"__init__".into() && !defined_paramter_name.contains(&zelf) {
|
||||
if name == &"__init__".into() && !defined_parameter_name.contains(&zelf) {
|
||||
return Err(format!(
|
||||
"__init__ method must have a `self` parameter (at {})",
|
||||
b.location
|
||||
));
|
||||
}
|
||||
if !defined_paramter_name.contains(&zelf) {
|
||||
if !defined_parameter_name.contains(&zelf) {
|
||||
return Err(format!(
|
||||
"class method must have a `self` parameter (at {})",
|
||||
b.location
|
||||
|
@ -1227,7 +1227,7 @@ impl TopLevelComposer {
|
|||
dummy_return_type
|
||||
} else {
|
||||
// if do not have return annotation, return none
|
||||
// for uniform handling, still use type annoatation
|
||||
// for uniform handling, still use type annotation
|
||||
let dummy_return_type = unifier.get_dummy_var().0;
|
||||
type_var_to_concrete_def.insert(
|
||||
dummy_return_type,
|
||||
|
@ -1468,7 +1468,7 @@ impl TopLevelComposer {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/// step 5, analyze and call type inferecer to fill the `instance_to_stmt` of topleveldef::function
|
||||
/// step 5, analyze and call type inferencer to fill the `instance_to_stmt` of topleveldef::function
|
||||
fn analyze_function_instance(&mut self) -> Result<(), String> {
|
||||
// first get the class contructor type correct for the following type check in function body
|
||||
// also do class field instantiation check
|
||||
|
@ -1792,7 +1792,6 @@ impl TopLevelComposer {
|
|||
result
|
||||
};
|
||||
let mut calls: HashMap<CodeLocation, CallId> = HashMap::new();
|
||||
let mut args = vec![];
|
||||
let mut inferencer = Inferencer {
|
||||
top_level: ctx.as_ref(),
|
||||
defined_identifiers: identifiers.clone(),
|
||||
|
@ -1813,7 +1812,6 @@ impl TopLevelComposer {
|
|||
result.insert("self".into(), self_ty);
|
||||
}
|
||||
result.extend(inst_args.iter().map(|x| (x.name, x.ty)));
|
||||
args.extend(result.iter().map(|(&a, &b)| (a, b)));
|
||||
result
|
||||
},
|
||||
primitives: primitives_ty,
|
||||
|
@ -1908,7 +1906,7 @@ impl TopLevelComposer {
|
|||
unreachable!("must be class id here")
|
||||
}
|
||||
},
|
||||
&mut |id| format!("tvar{}", id),
|
||||
&mut |id| format!("typevar{}", id),
|
||||
&mut None,
|
||||
);
|
||||
return Err(format!(
|
||||
|
@ -1919,18 +1917,6 @@ impl TopLevelComposer {
|
|||
));
|
||||
}
|
||||
|
||||
if simple_name.to_string() != "__init__" {
|
||||
EscapeAnalyzer::check_function_lifetime(
|
||||
unifier,
|
||||
&primitives_ty,
|
||||
resolver.as_ref().unwrap().clone(),
|
||||
ctx.as_ref(),
|
||||
&args,
|
||||
&fun_body,
|
||||
ast.as_ref().unwrap().location,
|
||||
).map_err(|e| format!("Escape analysis error: {}\n ... in function {}", e, name))?;
|
||||
}
|
||||
|
||||
instance_to_stmt.insert(
|
||||
get_subst_key(unifier, self_type, &subst, Some(&vars.keys().cloned().collect())),
|
||||
FunInstance {
|
||||
|
|
|
@ -149,7 +149,7 @@ impl TopLevelComposer {
|
|||
}
|
||||
|
||||
/// already include the definition_id of itself inside the ancestors vector
|
||||
/// when first regitering, the type_vars, fields, methods, ancestors are invalid
|
||||
/// when first registering, the type_vars, fields, methods, ancestors are invalid
|
||||
pub fn make_top_level_class_def(
|
||||
index: usize,
|
||||
resolver: Option<Arc<dyn SymbolResolver + Send + Sync>>,
|
||||
|
|
|
@ -9,7 +9,7 @@ expression: res_vec
|
|||
"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: [\"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",
|
||||
"Class {\nname: \"B\",\nancestors: [\"B[typevar7]\", \"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: [\"typevar7\"]\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: [\"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",
|
||||
|
|
|
@ -5,7 +5,7 @@ expression: res_vec
|
|||
|
||||
---
|
||||
[
|
||||
"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",
|
||||
"Class {\nname: \"A\",\nancestors: [\"A[typevar6, typevar7]\"],\nfields: [\"a\", \"b\"],\nmethods: [(\"__init__\", \"fn[[a:A[bool, float], b:B], none]\"), (\"fun\", \"fn[[a:A[bool, float]], A[bool, int32]]\")],\ntype_vars: [\"typevar6\", \"typevar7\"]\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: [\"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",
|
||||
|
|
|
@ -763,7 +763,7 @@ fn make_internal_resolver_with_tvar(
|
|||
(name, {
|
||||
let (ty, id) = unifier.get_fresh_var_with_range(range.as_slice(), None, None);
|
||||
if print {
|
||||
println!("{}: {:?}, tvar{}", name, ty, id);
|
||||
println!("{}: {:?}, typevar{}", name, ty, id);
|
||||
}
|
||||
ty
|
||||
})
|
||||
|
@ -791,7 +791,7 @@ impl<'a> Fold<Option<Type>> for TypeToStringFolder<'a> {
|
|||
self.unifier.internal_stringify(
|
||||
ty,
|
||||
&mut |id| format!("class{}", id.to_string()),
|
||||
&mut |id| format!("tvar{}", id.to_string()),
|
||||
&mut |id| format!("typevar{}", id.to_string()),
|
||||
&mut None,
|
||||
)
|
||||
} else {
|
||||
|
|
|
@ -356,7 +356,7 @@ pub fn get_type_from_type_annotation_kinds(
|
|||
unifier.internal_stringify(
|
||||
p,
|
||||
&mut |id| format!("class{}", id),
|
||||
&mut |id| format!("tvar{}", id),
|
||||
&mut |id| format!("typevar{}", id),
|
||||
&mut None
|
||||
),
|
||||
*id
|
||||
|
@ -436,7 +436,7 @@ pub fn get_type_from_type_annotation_kinds(
|
|||
/// the type of `self` should be similar to `A[T, V]`, where `T`, `V`
|
||||
/// considered to be type variables associated with the class \
|
||||
/// \
|
||||
/// But note that here we do not make a duplication of `T`, `V`, we direclty
|
||||
/// But note that here we do not make a duplication of `T`, `V`, we directly
|
||||
/// use them as they are in the TopLevelDef::Class since those in the
|
||||
/// TopLevelDef::Class.type_vars will be substitute later when seeing applications/instantiations
|
||||
/// the Type of their fields and methods will also be subst when application/instantiation
|
||||
|
|
|
@ -1,515 +0,0 @@
|
|||
use slab::Slab;
|
||||
use std::borrow::Cow;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use nac3parser::ast::{Location, StrRef};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum LifetimeKind {
|
||||
Static,
|
||||
NonLocal,
|
||||
Unknown,
|
||||
PreciseLocal,
|
||||
ImpreciseLocal,
|
||||
}
|
||||
|
||||
impl std::ops::BitAnd for LifetimeKind {
|
||||
type Output = Self;
|
||||
|
||||
fn bitand(self, rhs: Self) -> Self::Output {
|
||||
use LifetimeKind::*;
|
||||
match (self, rhs) {
|
||||
(x, y) if x == y => x,
|
||||
(PreciseLocal, ImpreciseLocal) | (ImpreciseLocal, PreciseLocal) => ImpreciseLocal,
|
||||
(Static, NonLocal) | (NonLocal, Static) => NonLocal,
|
||||
_ => Unknown,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct LifetimeId(usize);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub struct BasicBlockId(usize);
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum LifetimeIR {
|
||||
VarAssign { var: StrRef, lifetime: LifetimeId },
|
||||
VarAccess { var: StrRef },
|
||||
FieldAssign { obj: LifetimeId, field: StrRef, new: LifetimeId, is_init: bool },
|
||||
FieldAccess { obj: LifetimeId, field: StrRef },
|
||||
CreateLifetime { kind: LifetimeKind },
|
||||
PassedToFunc { param_lifetimes: Vec<LifetimeId> },
|
||||
UnifyLifetimes { lifetimes: Vec<LifetimeId> },
|
||||
Branch { targets: Vec<BasicBlockId> },
|
||||
Return { val: Option<LifetimeId> },
|
||||
}
|
||||
|
||||
pub struct LifetimeIRBuilder {
|
||||
irs: Vec<Option<(LifetimeIR, Location)>>,
|
||||
basic_blocks: Vec<Vec<usize>>,
|
||||
current_block: BasicBlockId,
|
||||
}
|
||||
|
||||
impl LifetimeIRBuilder {
|
||||
pub fn new() -> Self {
|
||||
LifetimeIRBuilder {
|
||||
irs: vec![None],
|
||||
basic_blocks: vec![vec![]],
|
||||
current_block: BasicBlockId(0),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn print_ir(&self) -> String {
|
||||
let mut lines = vec![];
|
||||
for (i, bb) in self.basic_blocks.iter().enumerate() {
|
||||
if bb.is_empty() {
|
||||
continue;
|
||||
}
|
||||
lines.push(format!("{}:", i));
|
||||
for ir in bb.iter() {
|
||||
if let Some((inst, loc)) = &self.irs[*ir] {
|
||||
lines.push(format!(" {}: {:?} ({})", *ir, inst, loc));
|
||||
}
|
||||
}
|
||||
}
|
||||
lines.join("\n")
|
||||
}
|
||||
|
||||
pub fn append_ir(&mut self, inst: LifetimeIR, loc: Location) -> LifetimeId {
|
||||
let id = self.irs.len();
|
||||
self.irs.push(Some((inst, loc)));
|
||||
self.basic_blocks[self.current_block.0].push(id);
|
||||
LifetimeId(id)
|
||||
}
|
||||
|
||||
pub fn append_block(&mut self) -> BasicBlockId {
|
||||
let id = self.basic_blocks.len();
|
||||
self.basic_blocks.push(vec![]);
|
||||
BasicBlockId(id)
|
||||
}
|
||||
|
||||
pub fn get_current_block(&self) -> BasicBlockId {
|
||||
self.current_block
|
||||
}
|
||||
|
||||
pub fn position_at_end(&mut self, id: BasicBlockId) {
|
||||
self.current_block = id;
|
||||
}
|
||||
|
||||
pub fn is_terminated(&self, id: BasicBlockId) -> bool {
|
||||
let bb = &self.basic_blocks[id.0];
|
||||
if bb.is_empty() {
|
||||
false
|
||||
} else {
|
||||
matches!(
|
||||
self.irs[*bb.last().unwrap()],
|
||||
Some((LifetimeIR::Return { .. }, _)) | Some((LifetimeIR::Branch { .. }, _))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_empty_bb(&mut self) {
|
||||
let mut destination_mapping = HashMap::new();
|
||||
let basic_blocks = &mut self.basic_blocks;
|
||||
let irs = &mut self.irs;
|
||||
for (i, bb) in basic_blocks.iter_mut().enumerate() {
|
||||
bb.retain(|&id| irs[id].is_some());
|
||||
if bb.len() == 1 {
|
||||
let id = bb.pop().unwrap();
|
||||
let ir = irs[id].take().unwrap();
|
||||
match ir.0 {
|
||||
LifetimeIR::Branch { targets } => {
|
||||
destination_mapping.insert(i, targets);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut buffer = HashSet::new();
|
||||
for bb in basic_blocks.iter_mut() {
|
||||
if bb.is_empty() {
|
||||
continue;
|
||||
}
|
||||
if let LifetimeIR::Branch { targets } =
|
||||
&mut irs[*bb.last().unwrap()].as_mut().unwrap().0
|
||||
{
|
||||
buffer.clear();
|
||||
let mut updated = false;
|
||||
for target in targets.iter() {
|
||||
if let Some(dest) = destination_mapping.get(&target.0) {
|
||||
buffer.extend(dest.iter().cloned());
|
||||
updated = true;
|
||||
} else {
|
||||
buffer.insert(*target);
|
||||
}
|
||||
}
|
||||
if updated {
|
||||
targets.clear();
|
||||
targets.extend(buffer.iter().cloned());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn analyze(&self) -> Result<(), String> {
|
||||
let mut analyzers = HashMap::new();
|
||||
analyzers.insert(0, (0, true, LifetimeAnalyzer::new()));
|
||||
let mut worklist = vec![0];
|
||||
while let Some(bb) = worklist.pop() {
|
||||
let (counter, updated, analyzer) = analyzers.get_mut(&bb).unwrap();
|
||||
*counter += 1;
|
||||
if *counter > 100 {
|
||||
return Err(format!("infinite loop detected at basic block {}", bb));
|
||||
}
|
||||
*updated = false;
|
||||
let mut analyzer = analyzer.clone();
|
||||
let block = &self.basic_blocks[bb];
|
||||
let ir_iter = block.iter().filter_map(|&id| {
|
||||
self.irs[id].as_ref().map(|(ir, loc)| (LifetimeId(id), ir, *loc))
|
||||
});
|
||||
if let Some(branch) = analyzer.analyze_basic_block(ir_iter)? {
|
||||
for &target in branch.iter() {
|
||||
if let Some((_, updated, successor)) = analyzers.get_mut(&target.0) {
|
||||
if successor.merge(&analyzer) && !*updated {
|
||||
// changed
|
||||
worklist.push(target.0);
|
||||
*updated = true;
|
||||
}
|
||||
} else {
|
||||
analyzers.insert(target.0, (0, true, analyzer.clone()));
|
||||
worklist.push(target.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
struct LifetimeStore {
|
||||
kind: LifetimeKind,
|
||||
fields: HashMap<StrRef, LifetimeId>,
|
||||
lifetimes: HashSet<LifetimeId>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct LifetimeAnalyzer<'a> {
|
||||
lifetime_to_id: HashMap<LifetimeId, usize>,
|
||||
lifetime_stores: Slab<Cow<'a, LifetimeStore>>,
|
||||
variable_assignment: HashMap<StrRef, LifetimeId>,
|
||||
}
|
||||
|
||||
impl<'a> LifetimeAnalyzer<'a> {
|
||||
pub fn new() -> Self {
|
||||
let mut zelf = LifetimeAnalyzer {
|
||||
lifetime_to_id: HashMap::new(),
|
||||
lifetime_stores: Default::default(),
|
||||
variable_assignment: HashMap::new(),
|
||||
};
|
||||
zelf.add_lifetime(LifetimeId(0), LifetimeKind::Unknown);
|
||||
zelf
|
||||
}
|
||||
|
||||
pub fn merge(&mut self, other: &LifetimeAnalyzer) -> bool {
|
||||
let mut to_be_merged = other.lifetime_to_id.keys().cloned().collect::<Vec<_>>();
|
||||
let mut updated = false;
|
||||
|
||||
let mut lifetime_merge_list = vec![];
|
||||
for (&var_name, &lifetime) in other.variable_assignment.iter() {
|
||||
if let Some(&our_lifetime) = self.variable_assignment.get(&var_name) {
|
||||
if our_lifetime != lifetime {
|
||||
lifetime_merge_list.push((our_lifetime, lifetime));
|
||||
}
|
||||
} else {
|
||||
self.variable_assignment.insert(var_name, lifetime);
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
while let Some(lifetime) = to_be_merged.pop() {
|
||||
let other_store_id = *other.lifetime_to_id.get(&lifetime).unwrap();
|
||||
if let Some(&self_store_id) = self.lifetime_to_id.get(&lifetime) {
|
||||
let self_store = self.lifetime_stores.get_mut(self_store_id).unwrap();
|
||||
let other_store = other.lifetime_stores.get(other_store_id).unwrap();
|
||||
let self_store = self_store.to_mut();
|
||||
// merge them
|
||||
for (&field, &other_lifetime) in other_store.fields.iter() {
|
||||
if let Some(&self_lifetime) = self_store.fields.get(&field) {
|
||||
if self_lifetime != other_lifetime {
|
||||
lifetime_merge_list.push((self_lifetime, other_lifetime));
|
||||
}
|
||||
} else {
|
||||
self_store.fields.insert(field, other_lifetime);
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
let zelf_lifetimes = &mut self_store.lifetimes;
|
||||
for &other_lifetime in other_store.lifetimes.iter() {
|
||||
if zelf_lifetimes.insert(other_lifetime) {
|
||||
lifetime_merge_list.push((lifetime, other_lifetime));
|
||||
}
|
||||
}
|
||||
let result_kind = self_store.kind & other_store.kind;
|
||||
if self_store.kind != result_kind {
|
||||
self_store.kind = result_kind;
|
||||
}
|
||||
} else {
|
||||
let store = other.lifetime_stores.get(other_store_id).unwrap().as_ref().clone();
|
||||
let store = self.lifetime_stores.insert(Cow::Owned(store));
|
||||
self.lifetime_to_id.insert(lifetime, store);
|
||||
updated = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (a, b) in lifetime_merge_list.into_iter() {
|
||||
self.unify(a, b);
|
||||
}
|
||||
|
||||
updated
|
||||
}
|
||||
|
||||
pub fn add_lifetime(&mut self, lifetime: LifetimeId, kind: LifetimeKind) {
|
||||
let id = self.lifetime_stores.insert(Cow::Owned(LifetimeStore {
|
||||
kind,
|
||||
fields: HashMap::new(),
|
||||
lifetimes: [lifetime].iter().cloned().collect(),
|
||||
}));
|
||||
let old_store_id = self.lifetime_to_id.insert(lifetime, id);
|
||||
if let Some(old_store_id) = old_store_id {
|
||||
let old_lifetime_store = self.lifetime_stores.get_mut(old_store_id).unwrap().to_mut();
|
||||
old_lifetime_store.lifetimes.remove(&lifetime);
|
||||
if old_lifetime_store.lifetimes.is_empty() {
|
||||
self.lifetime_stores.remove(old_store_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_lifetime(&mut self, lifetime: LifetimeId, to: LifetimeId) {
|
||||
let id = *self.lifetime_to_id.get(&to).unwrap();
|
||||
let store = self.lifetime_stores.get_mut(id).unwrap();
|
||||
store.to_mut().lifetimes.insert(lifetime);
|
||||
let old_store_id = self.lifetime_to_id.insert(lifetime, id);
|
||||
if let Some(old_store_id) = old_store_id {
|
||||
let old_lifetime_store = self.lifetime_stores.get_mut(old_store_id).unwrap().to_mut();
|
||||
old_lifetime_store.lifetimes.remove(&lifetime);
|
||||
if old_lifetime_store.lifetimes.is_empty() {
|
||||
self.lifetime_stores.remove(old_store_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn unify(&mut self, lhs: LifetimeId, rhs: LifetimeId) {
|
||||
use LifetimeKind::{ImpreciseLocal, PreciseLocal};
|
||||
let lhs_id = *self.lifetime_to_id.get(&lhs).unwrap();
|
||||
let rhs_id = *self.lifetime_to_id.get(&rhs).unwrap();
|
||||
if lhs_id == rhs_id {
|
||||
return;
|
||||
}
|
||||
let lhs_store = self.lifetime_stores.get(lhs_id).unwrap();
|
||||
let rhs_store = self.lifetime_stores.get(rhs_id).unwrap();
|
||||
let all_lifetimes: HashSet<_> =
|
||||
lhs_store.lifetimes.union(&rhs_store.lifetimes).cloned().collect();
|
||||
let result_kind = lhs_store.kind & rhs_store.kind;
|
||||
let fields = if matches!(result_kind, PreciseLocal | ImpreciseLocal) {
|
||||
let mut need_union = vec![];
|
||||
let mut fields = lhs_store.fields.clone();
|
||||
for (k, v) in rhs_store.fields.iter() {
|
||||
if let Some(old) = fields.insert(*k, *v) {
|
||||
need_union.push((old, *v));
|
||||
}
|
||||
}
|
||||
drop(lhs_store);
|
||||
drop(rhs_store);
|
||||
for (lhs, rhs) in need_union {
|
||||
self.unify(lhs, rhs);
|
||||
}
|
||||
fields
|
||||
} else {
|
||||
Default::default()
|
||||
};
|
||||
// unify them, slow
|
||||
for lifetime in all_lifetimes.iter() {
|
||||
self.lifetime_to_id.insert(*lifetime, lhs_id);
|
||||
}
|
||||
*self.lifetime_stores.get_mut(lhs_id).unwrap() =
|
||||
Cow::Owned(LifetimeStore { kind: result_kind, fields, lifetimes: all_lifetimes });
|
||||
self.lifetime_stores.remove(rhs_id);
|
||||
}
|
||||
|
||||
fn get_field_lifetime(&mut self, obj: LifetimeId, field: StrRef) -> LifetimeId {
|
||||
use LifetimeKind::*;
|
||||
let id = *self.lifetime_to_id.get(&obj).unwrap();
|
||||
let store = self.lifetime_stores.get(id).unwrap();
|
||||
if matches!(store.kind, PreciseLocal | ImpreciseLocal) {
|
||||
if let Some(&lifetime) = store.fields.get(&field) {
|
||||
let field_lifetime_kind = self.get_lifetime_kind(lifetime);
|
||||
if field_lifetime_kind == PreciseLocal
|
||||
&& (store.kind == ImpreciseLocal || field == "$elem".into())
|
||||
{
|
||||
let id = *self.lifetime_to_id.get(&lifetime).unwrap();
|
||||
self.lifetime_stores.get_mut(id).unwrap().to_mut().kind = ImpreciseLocal;
|
||||
}
|
||||
lifetime
|
||||
} else {
|
||||
LifetimeId(0)
|
||||
}
|
||||
} else {
|
||||
obj
|
||||
}
|
||||
}
|
||||
|
||||
fn set_field_lifetime(
|
||||
&mut self,
|
||||
obj: LifetimeId,
|
||||
field: StrRef,
|
||||
field_lifetime: LifetimeId,
|
||||
is_init: bool,
|
||||
) -> Result<(), String> {
|
||||
use LifetimeKind::*;
|
||||
let obj_id = *self.lifetime_to_id.get(&obj).unwrap();
|
||||
let field_id = *self.lifetime_to_id.get(&field_lifetime).unwrap();
|
||||
let field_lifetime_kind = self.lifetime_stores.get(field_id).unwrap().kind;
|
||||
let obj_store = self.lifetime_stores.get_mut(obj_id).unwrap();
|
||||
if !matches!(
|
||||
(obj_store.kind, field_lifetime_kind),
|
||||
(PreciseLocal, _) | (ImpreciseLocal, _) | (_, Static)
|
||||
) {
|
||||
return Err("field lifetime error".into());
|
||||
}
|
||||
match obj_store.kind {
|
||||
// $elem means list elements
|
||||
PreciseLocal if field != "$elem".into() => {
|
||||
// strong update
|
||||
obj_store.to_mut().fields.insert(field, field_lifetime);
|
||||
}
|
||||
PreciseLocal | ImpreciseLocal => {
|
||||
// weak update
|
||||
let old_lifetime = obj_store.to_mut().fields.get(&field).copied();
|
||||
if let Some(old_lifetime) = old_lifetime {
|
||||
self.unify(old_lifetime, field_lifetime);
|
||||
} else {
|
||||
obj_store.to_mut().fields.insert(field, field_lifetime);
|
||||
if !is_init {
|
||||
// unify with unknown lifetime
|
||||
self.unify(LifetimeId(0), field_lifetime);
|
||||
}
|
||||
if field == "$elem".into() {
|
||||
let field_lifetime_id = *self.lifetime_to_id.get(&field_lifetime).unwrap();
|
||||
let field_lifetime = self.lifetime_stores.get_mut(field_lifetime_id).unwrap();
|
||||
if field_lifetime.kind == PreciseLocal {
|
||||
field_lifetime.to_mut().kind = ImpreciseLocal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_lifetime_kind(&self, lifetime: LifetimeId) -> LifetimeKind {
|
||||
self.lifetime_stores.get(*self.lifetime_to_id.get(&lifetime).unwrap()).unwrap().kind
|
||||
}
|
||||
|
||||
fn pass_function_params(&mut self, lifetimes: &[LifetimeId]) {
|
||||
use LifetimeKind::*;
|
||||
let mut visited = HashSet::new();
|
||||
let mut worklist = vec![];
|
||||
|
||||
fn add_fields_to_worklist(
|
||||
visited: &mut HashSet<LifetimeId>,
|
||||
worklist: &mut Vec<(LifetimeId, bool)>,
|
||||
fields: &HashMap<StrRef, LifetimeId>,
|
||||
) {
|
||||
for (&name, &field) in fields.iter() {
|
||||
if visited.insert(field) {
|
||||
// not visited previously
|
||||
let name = name.to_string();
|
||||
let mutable = !(name.starts_with("$elem") && name.len() != "$elem".len());
|
||||
worklist.push((field, mutable));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for lifetime in lifetimes.iter() {
|
||||
let lifetime =
|
||||
self.lifetime_stores.get_mut(*self.lifetime_to_id.get(lifetime).unwrap()).unwrap();
|
||||
add_fields_to_worklist(&mut visited, &mut worklist, &lifetime.fields);
|
||||
}
|
||||
while let Some((item, mutable)) = worklist.pop() {
|
||||
let lifetime =
|
||||
self.lifetime_stores.get_mut(*self.lifetime_to_id.get(&item).unwrap()).unwrap();
|
||||
if matches!(lifetime.kind, Unknown | Static) {
|
||||
continue;
|
||||
}
|
||||
add_fields_to_worklist(&mut visited, &mut worklist, &lifetime.fields);
|
||||
if mutable {
|
||||
// we may assign values with static lifetime to function params
|
||||
lifetime.to_mut().kind = lifetime.kind & Static;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn analyze_basic_block<'b, I: Iterator<Item = (LifetimeId, &'b LifetimeIR, Location)>>(
|
||||
&mut self,
|
||||
instructions: I,
|
||||
) -> Result<Option<&'b [BasicBlockId]>, String> {
|
||||
use LifetimeIR::*;
|
||||
for (id, inst, loc) in instructions {
|
||||
match inst {
|
||||
VarAssign { var, lifetime } => {
|
||||
self.variable_assignment.insert(*var, *lifetime);
|
||||
}
|
||||
VarAccess { var } => {
|
||||
let lifetime = self.variable_assignment.get(var).cloned();
|
||||
if let Some(lifetime) = lifetime {
|
||||
self.set_lifetime(id, lifetime);
|
||||
} else {
|
||||
// should be static lifetime
|
||||
self.add_lifetime(id, LifetimeKind::Static)
|
||||
}
|
||||
}
|
||||
FieldAssign { obj, field, new, is_init } => {
|
||||
self.set_field_lifetime(*obj, *field, *new, *is_init)
|
||||
.map_err(|e| format!("{} in {}", e, loc))?;
|
||||
}
|
||||
FieldAccess { obj, field } => {
|
||||
let lifetime = self.get_field_lifetime(*obj, *field);
|
||||
self.set_lifetime(id, lifetime);
|
||||
}
|
||||
CreateLifetime { kind } => {
|
||||
if *kind == LifetimeKind::Unknown {
|
||||
self.set_lifetime(id, LifetimeId(0));
|
||||
} else {
|
||||
self.add_lifetime(id, *kind);
|
||||
}
|
||||
}
|
||||
PassedToFunc { param_lifetimes } => {
|
||||
self.pass_function_params(param_lifetimes);
|
||||
}
|
||||
UnifyLifetimes { lifetimes } => {
|
||||
assert!(!lifetimes.is_empty());
|
||||
let lhs = lifetimes[0];
|
||||
for rhs in lifetimes[1..].iter() {
|
||||
self.unify(lhs, *rhs);
|
||||
}
|
||||
self.set_lifetime(id, lhs);
|
||||
}
|
||||
Return { val } => {
|
||||
if let Some(val) = val {
|
||||
let kind = self.get_lifetime_kind(*val);
|
||||
if !matches!(kind, LifetimeKind::Static | LifetimeKind::NonLocal) {
|
||||
return Err(format!("return value lifetime error in {}", loc));
|
||||
}
|
||||
}
|
||||
return Ok(None);
|
||||
}
|
||||
Branch { targets } => return Ok(Some(targets)),
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
}
|
|
@ -1,580 +0,0 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use itertools::chain;
|
||||
use nac3parser::ast::{Comprehension, Constant, Expr, ExprKind, Location, Stmt, StmtKind, StrRef};
|
||||
|
||||
use lifetime::{BasicBlockId, LifetimeIR, LifetimeIRBuilder, LifetimeId, LifetimeKind};
|
||||
|
||||
use crate::{
|
||||
symbol_resolver::SymbolResolver,
|
||||
toplevel::{TopLevelContext, TopLevelDef},
|
||||
};
|
||||
|
||||
use super::{
|
||||
type_inferencer::PrimitiveStore,
|
||||
typedef::{Type, TypeEnum, Unifier},
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
mod lifetime;
|
||||
|
||||
pub struct EscapeAnalyzer<'a> {
|
||||
builder: LifetimeIRBuilder,
|
||||
loop_head: Option<BasicBlockId>,
|
||||
loop_tail: Option<BasicBlockId>,
|
||||
unifier: &'a mut Unifier,
|
||||
primitive_store: &'a PrimitiveStore,
|
||||
resolver: Arc<dyn SymbolResolver + Send + Sync>,
|
||||
top_level: &'a TopLevelContext,
|
||||
}
|
||||
|
||||
impl<'a> EscapeAnalyzer<'a> {
|
||||
pub fn new(
|
||||
unifier: &'a mut Unifier,
|
||||
primitive_store: &'a PrimitiveStore,
|
||||
resolver: Arc<dyn SymbolResolver + Send + Sync>,
|
||||
top_level: &'a TopLevelContext,
|
||||
) -> Self {
|
||||
Self {
|
||||
builder: LifetimeIRBuilder::new(),
|
||||
loop_head: None,
|
||||
loop_tail: None,
|
||||
primitive_store,
|
||||
unifier,
|
||||
resolver,
|
||||
top_level,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_function_lifetime(
|
||||
unifier: &'a mut Unifier,
|
||||
primitive_store: &'a PrimitiveStore,
|
||||
resolver: Arc<dyn SymbolResolver + Send + Sync>,
|
||||
top_level: &'a TopLevelContext,
|
||||
args: &[(StrRef, Type)],
|
||||
body: &[Stmt<Option<Type>>],
|
||||
loc: Location,
|
||||
) -> Result<(), String> {
|
||||
use LifetimeIR::{CreateLifetime, VarAssign};
|
||||
let mut zelf = Self::new(unifier, primitive_store, resolver, top_level);
|
||||
let nonlocal_lifetime =
|
||||
zelf.builder.append_ir(CreateLifetime { kind: LifetimeKind::NonLocal }, loc);
|
||||
for (name, ty) in args.iter().copied() {
|
||||
if zelf.need_alloca(ty) {
|
||||
zelf.builder.append_ir(VarAssign { var: name, lifetime: nonlocal_lifetime }, loc);
|
||||
}
|
||||
}
|
||||
zelf.handle_statements(body)?;
|
||||
zelf.builder.remove_empty_bb();
|
||||
zelf.builder.analyze().map_err(|e| {
|
||||
format!("{}\nIR: {}", e, zelf.builder.print_ir())
|
||||
})
|
||||
}
|
||||
|
||||
fn need_alloca(&mut self, ty: Type) -> bool {
|
||||
!(self.unifier.unioned(ty, self.primitive_store.int32)
|
||||
|| self.unifier.unioned(ty, self.primitive_store.int64)
|
||||
|| self.unifier.unioned(ty, self.primitive_store.uint32)
|
||||
|| self.unifier.unioned(ty, self.primitive_store.uint64)
|
||||
|| self.unifier.unioned(ty, self.primitive_store.float)
|
||||
|| self.unifier.unioned(ty, self.primitive_store.bool)
|
||||
|| self.unifier.unioned(ty, self.primitive_store.none)
|
||||
|| self.unifier.unioned(ty, self.primitive_store.range))
|
||||
}
|
||||
|
||||
fn is_terminated(&self) -> bool {
|
||||
self.builder.is_terminated(self.builder.get_current_block())
|
||||
}
|
||||
|
||||
fn handle_unknown_function_call<P: std::borrow::Borrow<Expr<Option<Type>>>>(
|
||||
&mut self,
|
||||
params: &[P],
|
||||
ret_need_alloca: bool,
|
||||
loc: Location,
|
||||
) -> Result<Option<LifetimeId>, String> {
|
||||
let param_lifetimes = params
|
||||
.iter()
|
||||
.filter_map(|p| self.handle_expr(p.borrow()).transpose())
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
self.builder.append_ir(LifetimeIR::PassedToFunc { param_lifetimes }, loc);
|
||||
if ret_need_alloca {
|
||||
Ok(Some(
|
||||
self.builder
|
||||
.append_ir(LifetimeIR::CreateLifetime { kind: LifetimeKind::Unknown }, loc),
|
||||
))
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_expr(&mut self, expr: &Expr<Option<Type>>) -> Result<Option<LifetimeId>, String> {
|
||||
use LifetimeIR::*;
|
||||
use LifetimeKind::*;
|
||||
let need_alloca = self.need_alloca(expr.custom.unwrap());
|
||||
let loc = expr.location;
|
||||
Ok(match &expr.node {
|
||||
ExprKind::Name { id, .. } => {
|
||||
if need_alloca {
|
||||
Some(self.builder.append_ir(VarAccess { var: *id }, loc))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
ExprKind::Attribute { value, attr, .. } => {
|
||||
if need_alloca {
|
||||
let val = self.handle_expr(value)?.unwrap();
|
||||
Some(self.builder.append_ir(FieldAccess { obj: val, field: *attr }, loc))
|
||||
} else {
|
||||
self.handle_expr(value)?;
|
||||
None
|
||||
}
|
||||
}
|
||||
ExprKind::Constant { .. } => {
|
||||
if need_alloca {
|
||||
Some(self.builder.append_ir(CreateLifetime { kind: Static }, loc))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
ExprKind::List { elts, .. } => {
|
||||
let elems =
|
||||
elts.iter().map(|e| self.handle_expr(e)).collect::<Result<Vec<_>, _>>()?;
|
||||
let list_lifetime =
|
||||
self.builder.append_ir(CreateLifetime { kind: PreciseLocal }, loc);
|
||||
if !elems.is_empty() {
|
||||
if elems[0].is_some() {
|
||||
let elems = elems.into_iter().map(|e| e.unwrap()).collect::<Vec<_>>();
|
||||
let elem_lifetime =
|
||||
self.builder.append_ir(UnifyLifetimes { lifetimes: elems }, loc);
|
||||
self.builder.append_ir(
|
||||
FieldAssign {
|
||||
obj: list_lifetime,
|
||||
field: "$elem".into(),
|
||||
new: elem_lifetime,
|
||||
is_init: true,
|
||||
},
|
||||
loc,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let elem_lifetime =
|
||||
self.builder.append_ir(CreateLifetime { kind: PreciseLocal }, loc);
|
||||
self.builder.append_ir(
|
||||
FieldAssign {
|
||||
obj: list_lifetime,
|
||||
field: "$elem".into(),
|
||||
new: elem_lifetime,
|
||||
is_init: true,
|
||||
},
|
||||
loc,
|
||||
);
|
||||
}
|
||||
Some(list_lifetime)
|
||||
}
|
||||
ExprKind::Tuple { elts, .. } => {
|
||||
let elems =
|
||||
elts.iter().map(|e| self.handle_expr(e)).collect::<Result<Vec<_>, _>>()?;
|
||||
let tuple_lifetime =
|
||||
self.builder.append_ir(CreateLifetime { kind: PreciseLocal }, loc);
|
||||
for (i, lifetime) in elems.into_iter().enumerate() {
|
||||
if let Some(lifetime) = lifetime {
|
||||
self.builder.append_ir(
|
||||
FieldAssign {
|
||||
obj: tuple_lifetime,
|
||||
field: format!("$elem{}", i).into(),
|
||||
new: lifetime,
|
||||
is_init: true,
|
||||
},
|
||||
loc,
|
||||
);
|
||||
}
|
||||
}
|
||||
Some(tuple_lifetime)
|
||||
}
|
||||
ExprKind::Subscript { value, slice, .. } => {
|
||||
let value_lifetime = self.handle_expr(value)?.unwrap();
|
||||
match &slice.node {
|
||||
ExprKind::Slice { lower, upper, step } => {
|
||||
for expr in [lower, upper, step].iter().filter_map(|x| x.as_ref()) {
|
||||
self.handle_expr(expr)?;
|
||||
}
|
||||
let slice_lifetime =
|
||||
self.builder.append_ir(CreateLifetime { kind: PreciseLocal }, loc);
|
||||
let slice_elem = self.builder.append_ir(
|
||||
FieldAccess { obj: value_lifetime, field: "$elem".into() },
|
||||
loc,
|
||||
);
|
||||
self.builder.append_ir(
|
||||
FieldAssign {
|
||||
obj: slice_lifetime,
|
||||
field: "$elem".into(),
|
||||
new: slice_elem,
|
||||
is_init: true
|
||||
},
|
||||
loc,
|
||||
);
|
||||
Some(slice_lifetime)
|
||||
}
|
||||
ExprKind::Constant { value: Constant::Int(v), .. }
|
||||
if matches!(
|
||||
&*self.unifier.get_ty(value.custom.unwrap()),
|
||||
TypeEnum::TTuple { .. }
|
||||
) =>
|
||||
{
|
||||
Some(self.builder.append_ir(
|
||||
FieldAccess {
|
||||
obj: value_lifetime,
|
||||
field: format!("$elem{}", v).into(),
|
||||
},
|
||||
loc,
|
||||
))
|
||||
}
|
||||
_ => {
|
||||
self.handle_expr(slice)?;
|
||||
if need_alloca {
|
||||
Some(self.builder.append_ir(
|
||||
FieldAccess { obj: value_lifetime, field: "$elem".into() },
|
||||
loc,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ExprKind::Call { func, args, keywords } => {
|
||||
let mut lifetimes = vec![];
|
||||
for arg in chain!(args.iter(), keywords.iter().map(|k| k.node.value.as_ref())) {
|
||||
if let Some(lifetime) = self.handle_expr(arg)? {
|
||||
lifetimes.push(lifetime);
|
||||
}
|
||||
}
|
||||
match &func.node {
|
||||
ExprKind::Name { id, .. } => {
|
||||
if !lifetimes.is_empty() {
|
||||
self.builder.append_ir(PassedToFunc { param_lifetimes: lifetimes }, loc);
|
||||
}
|
||||
if need_alloca {
|
||||
let id = self
|
||||
.resolver
|
||||
.get_identifier_def(*id)
|
||||
.map_err(|e| format!("{} (at {})", e, func.location))?;
|
||||
if let TopLevelDef::Class { .. } =
|
||||
&*self.top_level.definitions.read()[id.0].read()
|
||||
{
|
||||
Some(
|
||||
self.builder
|
||||
.append_ir(CreateLifetime { kind: PreciseLocal }, loc),
|
||||
)
|
||||
} else {
|
||||
Some(self.builder.append_ir(CreateLifetime { kind: Unknown }, loc))
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
ExprKind::Attribute { value, .. } => {
|
||||
let obj_lifetime = self.handle_expr(value)?.unwrap();
|
||||
lifetimes.push(obj_lifetime);
|
||||
self.builder.append_ir(PassedToFunc { param_lifetimes: lifetimes }, loc);
|
||||
if need_alloca {
|
||||
Some(self.builder.append_ir(CreateLifetime { kind: Unknown }, loc))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
ExprKind::BinOp { left, right, .. } => self.handle_unknown_function_call(
|
||||
&[left.as_ref(), right.as_ref()],
|
||||
need_alloca,
|
||||
loc,
|
||||
)?,
|
||||
ExprKind::BoolOp { values, .. } => {
|
||||
self.handle_unknown_function_call(&values, need_alloca, loc)?
|
||||
}
|
||||
ExprKind::UnaryOp { operand, .. } => {
|
||||
self.handle_unknown_function_call(&[operand.as_ref()], need_alloca, loc)?
|
||||
}
|
||||
ExprKind::Compare { left, comparators, .. } => {
|
||||
self.handle_unknown_function_call(&[left.as_ref()], false, loc)?;
|
||||
self.handle_unknown_function_call(&comparators, need_alloca, loc)?
|
||||
}
|
||||
ExprKind::IfExp { test, body, orelse } => {
|
||||
self.handle_expr(test)?;
|
||||
let body_bb = self.builder.append_block();
|
||||
let else_bb = self.builder.append_block();
|
||||
let tail_bb = self.builder.append_block();
|
||||
self.builder.append_ir(Branch { targets: vec![body_bb, else_bb] }, test.location);
|
||||
self.builder.position_at_end(body_bb);
|
||||
let body_lifetime = self.handle_expr(body)?;
|
||||
self.builder.append_ir(Branch { targets: vec![tail_bb] }, body.location);
|
||||
self.builder.position_at_end(else_bb);
|
||||
let else_lifetime = self.handle_expr(body)?;
|
||||
self.builder.append_ir(Branch { targets: vec![tail_bb] }, orelse.location);
|
||||
self.builder.position_at_end(tail_bb);
|
||||
if let (Some(body_lifetime), Some(else_lifetime)) = (body_lifetime, else_lifetime) {
|
||||
Some(self.builder.append_ir(
|
||||
UnifyLifetimes { lifetimes: vec![body_lifetime, else_lifetime] },
|
||||
loc,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
ExprKind::ListComp { elt, generators } => {
|
||||
let Comprehension { target, iter, ifs, .. } = &generators[0];
|
||||
let list_lifetime =
|
||||
self.builder.append_ir(CreateLifetime { kind: PreciseLocal }, loc);
|
||||
let iter_elem_lifetime = self.handle_expr(iter)?.map(|obj| {
|
||||
self.builder
|
||||
.append_ir(FieldAccess { obj, field: "$elem".into() }, iter.location)
|
||||
});
|
||||
let loop_body = self.builder.append_block();
|
||||
let loop_tail = self.builder.append_block();
|
||||
self.builder.append_ir(Branch { targets: vec![loop_body] }, loc);
|
||||
self.builder.position_at_end(loop_body);
|
||||
self.handle_assignment(target, iter_elem_lifetime)?;
|
||||
for ifexpr in ifs.iter() {
|
||||
self.handle_expr(ifexpr)?;
|
||||
}
|
||||
let elem_lifetime = self.handle_expr(elt)?;
|
||||
if let Some(elem_lifetime) = elem_lifetime {
|
||||
self.builder.append_ir(
|
||||
FieldAssign {
|
||||
obj: list_lifetime,
|
||||
field: "$elem".into(),
|
||||
new: elem_lifetime,
|
||||
is_init: true
|
||||
},
|
||||
elt.location,
|
||||
);
|
||||
}
|
||||
self.builder.append_ir(Branch { targets: vec![loop_body, loop_tail] }, loc);
|
||||
self.builder.position_at_end(loop_tail);
|
||||
Some(list_lifetime)
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
})
|
||||
}
|
||||
|
||||
fn handle_assignment(
|
||||
&mut self,
|
||||
lhs: &Expr<Option<Type>>,
|
||||
rhs_lifetime: Option<LifetimeId>,
|
||||
) -> Result<(), String> {
|
||||
use LifetimeIR::*;
|
||||
match &lhs.node {
|
||||
ExprKind::Attribute { value, attr, .. } => {
|
||||
let value_lifetime = self.handle_expr(value)?.unwrap();
|
||||
if let Some(field_lifetime) = rhs_lifetime {
|
||||
self.builder.append_ir(
|
||||
FieldAssign { obj: value_lifetime, field: *attr, new: field_lifetime, is_init: false },
|
||||
lhs.location,
|
||||
);
|
||||
}
|
||||
}
|
||||
ExprKind::Subscript { value, slice, .. } => {
|
||||
let value_lifetime = self.handle_expr(value)?.unwrap();
|
||||
let elem_lifetime = if let ExprKind::Slice { lower, upper, step } = &slice.node {
|
||||
for expr in [lower, upper, step].iter().filter_map(|x| x.as_ref()) {
|
||||
self.handle_expr(expr)?;
|
||||
}
|
||||
if let Some(rhs_lifetime) = rhs_lifetime {
|
||||
// must be a list
|
||||
Some(self.builder.append_ir(
|
||||
FieldAccess { obj: rhs_lifetime, field: "$elem".into() },
|
||||
lhs.location,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
self.handle_expr(slice)?;
|
||||
rhs_lifetime
|
||||
};
|
||||
// must be a list
|
||||
if let Some(elem_lifetime) = elem_lifetime {
|
||||
self.builder.append_ir(
|
||||
FieldAssign {
|
||||
obj: value_lifetime,
|
||||
field: "$elem".into(),
|
||||
new: elem_lifetime,
|
||||
is_init: false
|
||||
},
|
||||
lhs.location,
|
||||
);
|
||||
}
|
||||
}
|
||||
ExprKind::Name { id, .. } => {
|
||||
if let Some(lifetime) = rhs_lifetime {
|
||||
self.builder.append_ir(VarAssign { var: *id, lifetime }, lhs.location);
|
||||
}
|
||||
}
|
||||
ExprKind::Tuple { elts, .. } => {
|
||||
let rhs_lifetime = rhs_lifetime.unwrap();
|
||||
for (i, e) in elts.iter().enumerate() {
|
||||
let elem_lifetime = self.builder.append_ir(
|
||||
FieldAccess { obj: rhs_lifetime, field: format!("$elem{}", i).into() },
|
||||
e.location,
|
||||
);
|
||||
self.handle_assignment(e, Some(elem_lifetime))?;
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_statement(&mut self, stmt: &Stmt<Option<Type>>) -> Result<(), String> {
|
||||
use LifetimeIR::*;
|
||||
match &stmt.node {
|
||||
StmtKind::Expr { value, .. } => {
|
||||
self.handle_expr(value)?;
|
||||
}
|
||||
StmtKind::Assign { targets, value, .. } => {
|
||||
let rhs_lifetime = self.handle_expr(value)?;
|
||||
for target in targets {
|
||||
self.handle_assignment(target, rhs_lifetime)?;
|
||||
}
|
||||
}
|
||||
StmtKind::If { test, body, orelse, .. } => {
|
||||
// test should return bool
|
||||
self.handle_expr(test)?;
|
||||
let body_bb = self.builder.append_block();
|
||||
let else_bb = self.builder.append_block();
|
||||
self.builder.append_ir(Branch { targets: vec![body_bb, else_bb] }, stmt.location);
|
||||
self.builder.position_at_end(body_bb);
|
||||
self.handle_statements(&body)?;
|
||||
let body_terminated = self.is_terminated();
|
||||
if orelse.is_empty() {
|
||||
if !body_terminated {
|
||||
// else_bb is the basic block after this if statement
|
||||
self.builder.append_ir(Branch { targets: vec![else_bb] }, stmt.location);
|
||||
self.builder.position_at_end(else_bb);
|
||||
}
|
||||
} else {
|
||||
let tail_bb = self.builder.append_block();
|
||||
if !body_terminated {
|
||||
self.builder.append_ir(Branch { targets: vec![tail_bb] }, stmt.location);
|
||||
}
|
||||
self.builder.position_at_end(else_bb);
|
||||
self.handle_statements(&orelse)?;
|
||||
if !self.is_terminated() {
|
||||
self.builder.append_ir(Branch { targets: vec![tail_bb] }, stmt.location);
|
||||
}
|
||||
self.builder.position_at_end(tail_bb);
|
||||
}
|
||||
}
|
||||
StmtKind::While { test, body, orelse, .. } => {
|
||||
let old_loop_head = self.loop_head;
|
||||
let old_loop_tail = self.loop_tail;
|
||||
let loop_head = self.builder.append_block();
|
||||
let loop_body = self.builder.append_block();
|
||||
let loop_else =
|
||||
if orelse.is_empty() { None } else { Some(self.builder.append_block()) };
|
||||
let loop_tail = self.builder.append_block();
|
||||
self.loop_head = Some(loop_head);
|
||||
self.loop_tail = Some(loop_tail);
|
||||
self.builder.append_ir(Branch { targets: vec![loop_head] }, stmt.location);
|
||||
self.builder.position_at_end(loop_head);
|
||||
self.handle_expr(test)?;
|
||||
self.builder.append_ir(
|
||||
Branch { targets: vec![loop_body, loop_else.unwrap_or(loop_tail)] },
|
||||
stmt.location,
|
||||
);
|
||||
self.builder.position_at_end(loop_body);
|
||||
self.handle_statements(&body)?;
|
||||
if !self.is_terminated() {
|
||||
self.builder.append_ir(Branch { targets: vec![loop_head] }, stmt.location);
|
||||
}
|
||||
|
||||
self.loop_head = old_loop_head;
|
||||
self.loop_tail = old_loop_tail;
|
||||
if let Some(loop_else) = loop_else {
|
||||
self.builder.position_at_end(loop_else);
|
||||
self.handle_statements(&orelse)?;
|
||||
if !self.is_terminated() {
|
||||
self.builder.append_ir(Branch { targets: vec![loop_tail] }, stmt.location);
|
||||
}
|
||||
}
|
||||
self.builder.position_at_end(loop_tail);
|
||||
}
|
||||
StmtKind::For { target, iter, body, orelse, .. } => {
|
||||
let old_loop_head = self.loop_head;
|
||||
let old_loop_tail = self.loop_tail;
|
||||
let loop_head = self.builder.append_block();
|
||||
let loop_body = self.builder.append_block();
|
||||
let loop_else =
|
||||
if orelse.is_empty() { None } else { Some(self.builder.append_block()) };
|
||||
let loop_tail = self.builder.append_block();
|
||||
self.loop_head = Some(loop_head);
|
||||
self.loop_tail = Some(loop_tail);
|
||||
let iter_lifetime = self.handle_expr(iter)?.map(|obj| {
|
||||
self.builder
|
||||
.append_ir(FieldAccess { obj, field: "$elem".into() }, iter.location)
|
||||
});
|
||||
self.builder.append_ir(Branch { targets: vec![loop_head] }, stmt.location);
|
||||
self.builder.position_at_end(loop_head);
|
||||
if let Some(iter_lifetime) = iter_lifetime {
|
||||
self.handle_assignment(target, Some(iter_lifetime))?;
|
||||
}
|
||||
self.builder.append_ir(
|
||||
Branch { targets: vec![loop_body, loop_else.unwrap_or(loop_tail)] },
|
||||
stmt.location,
|
||||
);
|
||||
self.builder.position_at_end(loop_body);
|
||||
self.handle_statements(&body)?;
|
||||
if !self.is_terminated() {
|
||||
self.builder.append_ir(Branch { targets: vec![loop_head] }, stmt.location);
|
||||
}
|
||||
|
||||
self.loop_head = old_loop_head;
|
||||
self.loop_tail = old_loop_tail;
|
||||
if let Some(loop_else) = loop_else {
|
||||
self.builder.position_at_end(loop_else);
|
||||
self.handle_statements(&orelse)?;
|
||||
if !self.is_terminated() {
|
||||
self.builder.append_ir(Branch { targets: vec![loop_tail] }, stmt.location);
|
||||
}
|
||||
}
|
||||
self.builder.position_at_end(loop_tail);
|
||||
}
|
||||
|
||||
StmtKind::Continue { .. } => {
|
||||
if let Some(loop_head) = self.loop_head {
|
||||
self.builder.append_ir(Branch { targets: vec![loop_head] }, stmt.location);
|
||||
} else {
|
||||
return Err(format!("break outside loop"));
|
||||
}
|
||||
}
|
||||
StmtKind::Break { .. } => {
|
||||
if let Some(loop_tail) = self.loop_tail {
|
||||
self.builder.append_ir(Branch { targets: vec![loop_tail] }, stmt.location);
|
||||
} else {
|
||||
return Err(format!("break outside loop"));
|
||||
}
|
||||
}
|
||||
StmtKind::Return { value, .. } => {
|
||||
let val = if let Some(value) = value { self.handle_expr(value)? } else { None };
|
||||
self.builder.append_ir(Return { val }, stmt.location);
|
||||
}
|
||||
StmtKind::Pass { .. } => {}
|
||||
_ => unimplemented!("{:?}", stmt.node),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn handle_statements(&mut self, stmts: &[Stmt<Option<Type>>]) -> Result<(), String> {
|
||||
for stmt in stmts.iter() {
|
||||
if self.builder.is_terminated(self.builder.get_current_block()) {
|
||||
break;
|
||||
}
|
||||
self.handle_statement(stmt)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
use super::EscapeAnalyzer;
|
||||
use crate::typecheck::{type_inferencer::test::TestEnvironment, typedef::TypeEnum};
|
||||
use indoc::indoc;
|
||||
use nac3parser::ast::fold::Fold;
|
||||
use std::collections::hash_set::HashSet;
|
||||
use test_case::test_case;
|
||||
|
||||
use nac3parser::parser::parse_program;
|
||||
|
||||
#[test_case(indoc! {"
|
||||
# a: list[list[int32]]
|
||||
b = [1]
|
||||
a[0] = b
|
||||
"}, Err("field lifetime error in unknown: line 3 column 2".into())
|
||||
; "assign global elem")]
|
||||
#[test_case(indoc! {"
|
||||
# a: list[list[int32]]
|
||||
b = [[], []]
|
||||
b[1] = a
|
||||
b[0][0] = [0]
|
||||
"}, Err("field lifetime error in unknown: line 4 column 5".into())
|
||||
; "global unify")]
|
||||
#[test_case(indoc! {"
|
||||
b = [1, 2, 3]
|
||||
c = [a]
|
||||
c[0][0] = b
|
||||
"}, Err("field lifetime error in unknown: line 3 column 5".into())
|
||||
; "global unify 2")]
|
||||
fn test_simple(source: &str, expected_result: Result<(), String>) {
|
||||
let mut env = TestEnvironment::basic_test_env();
|
||||
let mut defined_identifiers: HashSet<_> = env.identifier_mapping.keys().cloned().collect();
|
||||
defined_identifiers.insert("a".into());
|
||||
let mut inferencer = env.get_inferencer();
|
||||
inferencer.defined_identifiers = defined_identifiers.clone();
|
||||
|
||||
let list_int = inferencer.unifier.add_ty(TypeEnum::TList { ty: inferencer.primitives.int32 });
|
||||
let list_list_int = inferencer.unifier.add_ty(TypeEnum::TList { ty: list_int });
|
||||
|
||||
inferencer.variable_mapping.insert("a".into(), list_list_int);
|
||||
let statements = parse_program(source, Default::default()).unwrap();
|
||||
let statements = statements
|
||||
.into_iter()
|
||||
.map(|v| inferencer.fold_stmt(v))
|
||||
.collect::<Result<Vec<_>, _>>()
|
||||
.unwrap();
|
||||
|
||||
inferencer.check_block(&statements, &mut defined_identifiers).unwrap();
|
||||
|
||||
let mut lifetime_ctx = EscapeAnalyzer::new(
|
||||
&mut inferencer.unifier,
|
||||
&mut inferencer.primitives,
|
||||
inferencer.function_data.resolver.clone(),
|
||||
&inferencer.top_level,
|
||||
);
|
||||
lifetime_ctx.handle_statements(&statements).unwrap();
|
||||
lifetime_ctx.builder.remove_empty_bb();
|
||||
let result = lifetime_ctx.builder.analyze();
|
||||
assert_eq!(result, expected_result);
|
||||
|
||||
}
|
|
@ -4,4 +4,3 @@ pub mod type_error;
|
|||
pub mod type_inferencer;
|
||||
pub mod typedef;
|
||||
mod unification_table;
|
||||
pub mod escape_analysis;
|
||||
|
|
|
@ -14,7 +14,7 @@ use nac3parser::ast::{
|
|||
};
|
||||
|
||||
#[cfg(test)]
|
||||
pub(crate) mod test;
|
||||
mod test;
|
||||
|
||||
#[derive(PartialEq, Eq, Hash, Copy, Clone, Debug)]
|
||||
pub struct CodeLocation {
|
||||
|
|
|
@ -11,7 +11,7 @@ use nac3parser::parser::parse_program;
|
|||
use parking_lot::RwLock;
|
||||
use test_case::test_case;
|
||||
|
||||
pub(crate) struct Resolver {
|
||||
struct Resolver {
|
||||
id_to_type: HashMap<StrRef, Type>,
|
||||
id_to_def: HashMap<StrRef, DefinitionId>,
|
||||
class_names: HashMap<StrRef, Type>,
|
||||
|
@ -56,7 +56,7 @@ impl SymbolResolver for Resolver {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) struct TestEnvironment {
|
||||
struct TestEnvironment {
|
||||
pub unifier: Unifier,
|
||||
pub function_data: FunctionData,
|
||||
pub primitives: PrimitiveStore,
|
||||
|
@ -192,7 +192,7 @@ impl TestEnvironment {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn new() -> TestEnvironment {
|
||||
fn new() -> TestEnvironment {
|
||||
let mut unifier = Unifier::new();
|
||||
let mut identifier_mapping = HashMap::new();
|
||||
let mut top_level_defs: Vec<Arc<RwLock<TopLevelDef>>> = Vec::new();
|
||||
|
@ -447,7 +447,7 @@ impl TestEnvironment {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_inferencer(&mut self) -> Inferencer {
|
||||
fn get_inferencer(&mut self) -> Inferencer {
|
||||
Inferencer {
|
||||
top_level: &self.top_level,
|
||||
function_data: &mut self.function_data,
|
||||
|
|
|
@ -16,7 +16,7 @@ use crate::toplevel::{DefinitionId, TopLevelContext, TopLevelDef};
|
|||
#[cfg(test)]
|
||||
mod test;
|
||||
|
||||
/// Handle for a type, implementated as a key in the unification table.
|
||||
/// Handle for a type, implemented as a key in the unification table.
|
||||
pub type Type = UnificationKey;
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
|
@ -830,7 +830,7 @@ impl Unifier {
|
|||
},
|
||||
)
|
||||
},
|
||||
&mut |id| format!("var{}", id),
|
||||
&mut |id| format!("typevar{}", id),
|
||||
notes,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -302,14 +302,14 @@ fn test_unify(
|
|||
("v1", "Record[a=float,b=int]"),
|
||||
("v2", "Foo[v3]"),
|
||||
],
|
||||
(("v1", "v2"), "`3[var4]::b` field/method does not exist")
|
||||
(("v1", "v2"), "`3[typevar4]::b` field/method does not exist")
|
||||
; "record obj merge"
|
||||
)]
|
||||
/// Test cases for invalid unifications.
|
||||
fn test_invalid_unification(
|
||||
variable_count: u32,
|
||||
unify_pairs: &[(&'static str, &'static str)],
|
||||
errornous_pair: ((&'static str, &'static str), &'static str),
|
||||
erroneous_pair: ((&'static str, &'static str), &'static str),
|
||||
) {
|
||||
let mut env = TestEnvironment::new();
|
||||
let mut mapping = HashMap::new();
|
||||
|
@ -326,11 +326,11 @@ fn test_invalid_unification(
|
|||
pairs.push((t1, t2));
|
||||
}
|
||||
let (t1, t2) =
|
||||
(env.parse(errornous_pair.0 .0, &mapping), env.parse(errornous_pair.0 .1, &mapping));
|
||||
(env.parse(erroneous_pair.0 .0, &mapping), env.parse(erroneous_pair.0 .1, &mapping));
|
||||
for (a, b) in pairs {
|
||||
env.unifier.unify(a, b).unwrap();
|
||||
}
|
||||
assert_eq!(env.unify(t1, t2), Err(errornous_pair.1.to_string()));
|
||||
assert_eq!(env.unify(t1, t2), Err(erroneous_pair.1.to_string()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -445,7 +445,7 @@ fn test_typevar_range() {
|
|||
// where v in (int, list[v1]), v1 in (int, bool)
|
||||
assert_eq!(
|
||||
env.unify(float_list, v),
|
||||
Err("Expected any one of these types: 0, list[var5], but got list[1]\n\nNotes:\n var5 ∈ {0, 2}".to_string())
|
||||
Err("Expected any one of these types: 0, list[typevar5], but got list[1]\n\nNotes:\n typevar5 ∈ {0, 2}".to_string())
|
||||
);
|
||||
|
||||
let a = env.unifier.get_fresh_var_with_range(&[int, float], None, None).0;
|
||||
|
@ -504,9 +504,9 @@ fn test_rigid_var() {
|
|||
let int = env.parse("int", &HashMap::new());
|
||||
let list_int = env.parse("list[int]", &HashMap::new());
|
||||
|
||||
assert_eq!(env.unify(a, b), Err("Incompatible types: var3 and var2".to_string()));
|
||||
assert_eq!(env.unify(a, b), Err("Incompatible types: typevar3 and typevar2".to_string()));
|
||||
env.unifier.unify(list_a, list_x).unwrap();
|
||||
assert_eq!(env.unify(list_x, list_int), Err("Incompatible types: 0 and var2".to_string()));
|
||||
assert_eq!(env.unify(list_x, list_int), Err("Incompatible types: 0 and typevar2".to_string()));
|
||||
|
||||
env.unifier.replace_rigid_var(a, int);
|
||||
env.unifier.unify(list_x, list_int).unwrap();
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::rc::Rc;
|
|||
use itertools::izip;
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
|
||||
pub struct UnificationKey(pub(crate) usize);
|
||||
pub struct UnificationKey(usize);
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct UnificationTable<V> {
|
||||
|
@ -44,12 +44,6 @@ impl<V> UnificationTable<V> {
|
|||
UnificationTable { parents: Vec::new(), ranks: Vec::new(), values: Vec::new(), log: Vec::new(), generation: 0 }
|
||||
}
|
||||
|
||||
fn log_action(&mut self, action: Action<V>) {
|
||||
if !self.log.is_empty() {
|
||||
self.log.push(action);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_key(&mut self, v: V) -> UnificationKey {
|
||||
let index = self.parents.len();
|
||||
self.parents.push(index);
|
||||
|
@ -67,10 +61,10 @@ impl<V> UnificationTable<V> {
|
|||
if self.ranks[a] < self.ranks[b] {
|
||||
std::mem::swap(&mut a, &mut b);
|
||||
}
|
||||
self.log_action(Action::Parent { key: b, original_parent: self.parents[b] });
|
||||
self.log.push(Action::Parent { key: b, original_parent: self.parents[b] });
|
||||
self.parents[b] = a;
|
||||
if self.ranks[a] == self.ranks[b] {
|
||||
self.log_action(Action::Rank { key: a, original_rank: self.ranks[a] });
|
||||
self.log.push(Action::Rank { key: a, original_rank: self.ranks[a] });
|
||||
self.ranks[a] += 1;
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +88,7 @@ impl<V> UnificationTable<V> {
|
|||
pub fn set_value(&mut self, a: UnificationKey, v: V) {
|
||||
let index = self.find(a);
|
||||
let original_value = self.values[index].replace(v);
|
||||
self.log_action(Action::Value { key: index, original_value });
|
||||
self.log.push(Action::Value { key: index, original_value });
|
||||
}
|
||||
|
||||
pub fn unioned(&mut self, a: UnificationKey, b: UnificationKey) -> bool {
|
||||
|
@ -112,7 +106,7 @@ impl<V> UnificationTable<V> {
|
|||
// a = parent.parent
|
||||
let a = self.parents[parent];
|
||||
// root.parent = parent.parent
|
||||
self.log_action(Action::Parent { key: root, original_parent: self.parents[root] });
|
||||
self.log.push(Action::Parent { key: root, original_parent: self.parents[root] });
|
||||
self.parents[root] = a;
|
||||
root = parent;
|
||||
// parent = root.parent
|
||||
|
|
|
@ -229,7 +229,7 @@ def list_slice_assignment():
|
|||
bl5[3:-5] = []
|
||||
output_int32_list([int32(b) for b in bl5])
|
||||
bl6 = bl[:]
|
||||
bl6[3:-5] = [True, False, False]
|
||||
bl6[3:-5] = [True, False]
|
||||
output_int32_list([int32(b) for b in bl6])
|
||||
bl7 = bl[:]
|
||||
bl7[:-2] = [False]
|
||||
|
|
|
@ -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
|
|
@ -220,11 +220,7 @@ fn main() {
|
|||
let signature = store.from_signature(&mut composer.unifier, &primitive, &signature, &mut cache);
|
||||
let signature = store.add_cty(signature);
|
||||
|
||||
if let Err(e) = composer.start_analysis(true) {
|
||||
eprintln!("{}", e);
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
composer.start_analysis(true).unwrap();
|
||||
|
||||
let top_level = Arc::new(composer.make_top_level_context());
|
||||
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
pkgbase="mingw-w64-nac3artiq"
|
||||
pkgname="mingw-w64-x86_64-nac3artiq"
|
||||
pkgver=1.0
|
||||
pkgrel=1
|
||||
pkgdesc="New ARTIQ compiler 3"
|
||||
arch=("any")
|
||||
mingw_arch=("mingw64")
|
||||
url="https://m-labs.hk"
|
||||
license=("LGPL")
|
||||
source=("nac3artiq.pyd")
|
||||
noextract=("nac3artiq.pyd")
|
||||
sha256sums=("SKIP")
|
||||
depends=("mingw-w64-x86_64-python")
|
||||
|
||||
prepare() {
|
||||
true
|
||||
}
|
||||
|
||||
build() {
|
||||
true
|
||||
}
|
||||
|
||||
package() {
|
||||
mkdir -p $pkgdir/mingw64/lib/python3.9/site-packages
|
||||
cp ${srcdir}/nac3artiq.pyd $pkgdir/mingw64/lib/python3.9/site-packages
|
||||
}
|
|
@ -80,7 +80,7 @@ in rec {
|
|||
name = "nac3artiq-msys2";
|
||||
src = ../../.;
|
||||
cargoLock = { lockFile = ../../Cargo.lock; };
|
||||
nativeBuildInputs = [ pkgs.wineWowPackages.stable pkgs.zip ];
|
||||
nativeBuildInputs = [ pkgs.wineWowPackages.stable ];
|
||||
buildPhase =
|
||||
''
|
||||
export HOME=`mktemp -d`
|
||||
|
@ -92,10 +92,9 @@ in rec {
|
|||
'';
|
||||
installPhase =
|
||||
''
|
||||
mkdir -p $out $out/nix-support
|
||||
ln -s target/release/nac3artiq.dll nac3artiq.pyd
|
||||
zip $out/nac3artiq.zip nac3artiq.pyd
|
||||
echo file binary-dist $out/nac3artiq.zip >> $out/nix-support/hydra-build-products
|
||||
mkdir $out $out/nix-support
|
||||
cp target/release/nac3artiq.dll $out/nac3artiq.pyd
|
||||
echo file binary-dist $out/nac3artiq.pyd >> $out/nix-support/hydra-build-products
|
||||
'';
|
||||
checkPhase =
|
||||
''
|
||||
|
@ -103,6 +102,24 @@ in rec {
|
|||
'';
|
||||
dontFixup = true;
|
||||
};
|
||||
nac3artiq-pkg = pkgs.stdenvNoCC.mkDerivation {
|
||||
name = "nac3artiq-msys2-pkg";
|
||||
nativeBuildInputs = [ pkgs.pacman pkgs.fakeroot pkgs.libarchive pkgs.zstd ];
|
||||
src = nac3artiq;
|
||||
phases = [ "buildPhase" "installPhase" ];
|
||||
buildPhase =
|
||||
''
|
||||
ln -s ${./PKGBUILD} PKGBUILD
|
||||
ln -s $src/nac3artiq.pyd nac3artiq.pyd
|
||||
makepkg --config ${./makepkg.conf} --nodeps
|
||||
'';
|
||||
installPhase =
|
||||
''
|
||||
mkdir $out $out/nix-support
|
||||
cp *.pkg.tar.zst $out
|
||||
echo file msys2 $out/*.pkg.tar.zst >> $out/nix-support/hydra-build-products
|
||||
'';
|
||||
};
|
||||
lld = pkgs.stdenvNoCC.mkDerivation rec {
|
||||
pname = "lld-msys2";
|
||||
version = "13.0.1";
|
||||
|
@ -142,4 +159,12 @@ in rec {
|
|||
export PYO3_CONFIG_FILE=Z:${pyo3-mingw-config}
|
||||
exec ${pkgs.wineWowPackages.stable}/bin/wine64 cmd
|
||||
'';
|
||||
wine-msys2-build = pkgs.writeShellScriptBin "wine-msys2-build"
|
||||
''
|
||||
export HOME=`mktemp -d`
|
||||
export WINEDEBUG=-all
|
||||
export WINEPATH=Z:${msys2-env}/mingw64/bin
|
||||
${silenceFontconfig}
|
||||
exec ${pkgs.wineWowPackages.stable}/bin/wine64 $@
|
||||
'';
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ curl -L https://mirror.msys2.org/msys/x86_64/pacman-mirrors-20220205-1-any.pkg.t
|
|||
curl -L https://raw.githubusercontent.com/msys2/MSYS2-packages/master/pacman/pacman.conf | grep -v SigLevel | sed s\|/etc/pacman.d\|$MSYS2DIR/etc/pacman.d\|g > $MSYS2DIR/etc/pacman.conf
|
||||
|
||||
fakeroot pacman --root $MSYS2DIR --config $MSYS2DIR/etc/pacman.conf -Syy
|
||||
pacman --root $MSYS2DIR --config $MSYS2DIR/etc/pacman.conf --cachedir $MSYS2DIR/msys/cache -Sp mingw-w64-x86_64-rust mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-python3.9 mingw-w64-x86_64-python-numpy > $MSYS2DIR/packages.txt
|
||||
pacman --root $MSYS2DIR --config $MSYS2DIR/etc/pacman.conf --cachedir $MSYS2DIR/msys/cache -Sp mingw-w64-x86_64-rust mingw-w64-x86_64-cmake mingw-w64-x86_64-ninja mingw-w64-x86_64-python3.9 mingw-w64-x86_64-python-numpy mingw-w64-x86_64-python-setuptools > $MSYS2DIR/packages.txt
|
||||
|
||||
echo "{ pkgs } : [" > msys2_packages.nix
|
||||
while read package; do
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
PKGEXT='.pkg.tar.zst'
|
||||
SRCEXT='.src.tar.gz'
|
|
@ -6,8 +6,8 @@
|
|||
})
|
||||
|
||||
(pkgs.fetchurl {
|
||||
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-zlib-1.2.11-9-any.pkg.tar.zst";
|
||||
sha256 = "0fb3xbw9a0ah4viwp3a7hr5phnc7mvcl9ba2yjidncpqmspypacx";
|
||||
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-zlib-1.2.12-1-any.pkg.tar.zst";
|
||||
sha256 = "1b461ic5s3hjk3y70ldik82ny08rdywn1zfqa8d2jyyvnh4dya77";
|
||||
})
|
||||
|
||||
(pkgs.fetchurl {
|
||||
|
@ -16,13 +16,13 @@
|
|||
})
|
||||
|
||||
(pkgs.fetchurl {
|
||||
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-headers-git-9.0.0.6451.a3f6d363d-1-any.pkg.tar.zst";
|
||||
sha256 = "1ngnjb9vgk295wlwqandm0nhqqdfrp584kx3nfshxkhwmj5gpzxk";
|
||||
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-headers-git-9.0.0.6454.b4445ee52-1-any.pkg.tar.zst";
|
||||
sha256 = "0wyg5ad3fh2lwd7avxvpncipj5wxmp647l43wzr1l3rrkd820yy3";
|
||||
})
|
||||
|
||||
(pkgs.fetchurl {
|
||||
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-crt-git-9.0.0.6451.a3f6d363d-1-any.pkg.tar.zst";
|
||||
sha256 = "1ccipidbsjncdhr48k50ia53dwn7v3ghdl8f1svgwvnh3mrx0bww";
|
||||
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-crt-git-9.0.0.6454.b4445ee52-1-any.pkg.tar.zst";
|
||||
sha256 = "0bnzwgf395fbwbsq8900prj409b081hi0dd76kak6d971xqyy2r4";
|
||||
})
|
||||
|
||||
(pkgs.fetchurl {
|
||||
|
@ -46,8 +46,8 @@
|
|||
})
|
||||
|
||||
(pkgs.fetchurl {
|
||||
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-libwinpthread-git-9.0.0.6451.a3f6d363d-1-any.pkg.tar.zst";
|
||||
sha256 = "0qdy79l5y02lw2xa8i3j6yayhz8a7awfgyyd82pcmbzwx57q2xqb";
|
||||
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-libwinpthread-git-9.0.0.6454.b4445ee52-1-any.pkg.tar.zst";
|
||||
sha256 = "181fm72bi6cs348hixkqjzivizzcsyn2lxvnbqprx4366prjf7nn";
|
||||
})
|
||||
|
||||
(pkgs.fetchurl {
|
||||
|
@ -61,8 +61,8 @@
|
|||
})
|
||||
|
||||
(pkgs.fetchurl {
|
||||
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-winpthreads-git-9.0.0.6451.a3f6d363d-1-any.pkg.tar.zst";
|
||||
sha256 = "08zwgkrp45y5ry8avz61krasjkk4k4a5rrdz4nd78bbbah84mpgz";
|
||||
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-winpthreads-git-9.0.0.6454.b4445ee52-1-any.pkg.tar.zst";
|
||||
sha256 = "0n3pim85wlsc93y9sh06rnfraqgzbz300sp9hd8n7wgvcsmpj9rx";
|
||||
})
|
||||
|
||||
(pkgs.fetchurl {
|
||||
|
@ -86,8 +86,8 @@
|
|||
})
|
||||
|
||||
(pkgs.fetchurl {
|
||||
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-expat-2.4.7-1-any.pkg.tar.zst";
|
||||
sha256 = "19qh3kk2kmkkzxirpx1swgfsfb29gy9q8qgdmrzzbwrqrn8vs77j";
|
||||
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-expat-2.4.8-1-any.pkg.tar.zst";
|
||||
sha256 = "1qkw4k61ddaflns5ms0xh0czbx99wxhs0dfbk8sv8by2rkshl51k";
|
||||
})
|
||||
|
||||
(pkgs.fetchurl {
|
||||
|
@ -101,8 +101,8 @@
|
|||
})
|
||||
|
||||
(pkgs.fetchurl {
|
||||
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-libidn2-2.3.1-1-any.pkg.tar.zst";
|
||||
sha256 = "00vm6d56ldr1f4h0dn15j0ja17dif45qxlxaqv4x5nw555frklf5";
|
||||
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-libidn2-2.3.2-1-any.pkg.tar.zst";
|
||||
sha256 = "0p694g8g716zyvcxc48xmdanjyrkp3ir4naqlradrw2szy9bj800";
|
||||
})
|
||||
|
||||
(pkgs.fetchurl {
|
||||
|
@ -231,8 +231,8 @@
|
|||
})
|
||||
|
||||
(pkgs.fetchurl {
|
||||
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-cmake-3.22.3-2-any.pkg.tar.zst";
|
||||
sha256 = "1xp8n5s98va7a9cq36d9p49lk32yv9vkvlayc4d4j465xzm21hgp";
|
||||
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-cmake-3.23.0-1-any.pkg.tar.zst";
|
||||
sha256 = "1gcmm3bd29zfd88ack6xdqwmbskyvhb4ymrfspayk4jfcj4svky5";
|
||||
})
|
||||
|
||||
(pkgs.fetchurl {
|
||||
|
@ -266,8 +266,8 @@
|
|||
})
|
||||
|
||||
(pkgs.fetchurl {
|
||||
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-sqlite3-3.38.1-1-any.pkg.tar.zst";
|
||||
sha256 = "04h4m72mwmad82nzrl5qj9wlsinjs7z7bsbbq17dxkx92aj75p26";
|
||||
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-sqlite3-3.38.2-1-any.pkg.tar.zst";
|
||||
sha256 = "14agi4611h5400j7bqh1w5i5qbhp4hp6apzaddilxq97y37vj90q";
|
||||
})
|
||||
|
||||
(pkgs.fetchurl {
|
||||
|
@ -291,12 +291,17 @@
|
|||
})
|
||||
|
||||
(pkgs.fetchurl {
|
||||
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-openblas-0.3.20-1-any.pkg.tar.zst";
|
||||
sha256 = "1adbbycbvs2nkjhgap92fk3x0vqfjb3ghhvyd1xlnn56c5n0iphf";
|
||||
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-openblas-0.3.20-2-any.pkg.tar.zst";
|
||||
sha256 = "1cj0j06b2xhcvq9v6jx5cn130r8pkv2xa885f3z3z98kmrifjw0l";
|
||||
})
|
||||
|
||||
(pkgs.fetchurl {
|
||||
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-python-numpy-1.21.5-1-any.pkg.tar.zst";
|
||||
sha256 = "10bhfq65nrzxipgy75bqaad74daif4ay06phwvbx70b9j0wm33c3";
|
||||
})
|
||||
|
||||
(pkgs.fetchurl {
|
||||
url = "https://mirror.msys2.org/mingw/mingw64/mingw-w64-x86_64-python-setuptools-59.8.0-2-any.pkg.tar.zst";
|
||||
sha256 = "1jbvsmh1r00yb6fm8by6z7xp9069f9lkj73jrsnxmflmvpdkjl5c";
|
||||
})
|
||||
]
|
||||
|
|
Loading…
Reference in New Issue