Support operator checks for user-defined classes

This commit is contained in:
Shunsuke Shibayama 2023-02-24 14:57:08 +09:00
parent f360c2c763
commit 4b3ff53295
5 changed files with 39 additions and 16 deletions

12
Cargo.lock generated
View file

@ -253,7 +253,7 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
[[package]]
name = "els"
version = "0.1.18"
source = "git+https://github.com/erg-lang/erg?branch=main#23cbbe30719af994dc299438f9b35acef4ddc19c"
source = "git+https://github.com/erg-lang/erg?branch=py-structural#5cddba369e9124f846348e0a690d3b84b252366a"
dependencies = [
"erg_common",
"erg_compiler",
@ -274,7 +274,7 @@ dependencies = [
[[package]]
name = "erg_common"
version = "0.6.6"
source = "git+https://github.com/erg-lang/erg?branch=main#23cbbe30719af994dc299438f9b35acef4ddc19c"
source = "git+https://github.com/erg-lang/erg?branch=py-structural#5cddba369e9124f846348e0a690d3b84b252366a"
dependencies = [
"backtrace-on-stack-overflow",
"hermit-abi",
@ -285,7 +285,7 @@ dependencies = [
[[package]]
name = "erg_compiler"
version = "0.6.6"
source = "git+https://github.com/erg-lang/erg?branch=main#23cbbe30719af994dc299438f9b35acef4ddc19c"
source = "git+https://github.com/erg-lang/erg?branch=py-structural#5cddba369e9124f846348e0a690d3b84b252366a"
dependencies = [
"erg_common",
"erg_parser",
@ -294,7 +294,7 @@ dependencies = [
[[package]]
name = "erg_parser"
version = "0.6.6"
source = "git+https://github.com/erg-lang/erg?branch=main#23cbbe30719af994dc299438f9b35acef4ddc19c"
source = "git+https://github.com/erg-lang/erg?branch=py-structural#5cddba369e9124f846348e0a690d3b84b252366a"
dependencies = [
"erg_common",
"unicode-xid 0.2.4",
@ -916,9 +916,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.107"
version = "1.0.108"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
checksum = "d56e159d99e6c2b93995d171050271edb50ecc5288fbc7cc17de8fdce4e58c14"
dependencies = [
"proc-macro2",
"quote",

View file

@ -26,9 +26,9 @@ repository = "https://github.com/mtshiba/pylyzer"
# erg_compiler = { version = "0.6.5-nightly.1", features = ["py_compatible", "els"] }
# els = { version = "0.1.17-nightly.1", features = ["py_compatible"] }
rustpython-parser = "0.1.2"
erg_compiler = { git = "https://github.com/erg-lang/erg", branch = "main", features = ["py_compatible", "els"] }
erg_common = { git = "https://github.com/erg-lang/erg", branch = "main", features = ["py_compatible", "els"] }
els = { git = "https://github.com/erg-lang/erg", branch = "main", features = ["py_compatible"] }
erg_compiler = { git = "https://github.com/erg-lang/erg", branch = "py-structural", features = ["py_compatible", "els"] }
erg_common = { git = "https://github.com/erg-lang/erg", branch = "py-structural", features = ["py_compatible", "els"] }
els = { git = "https://github.com/erg-lang/erg", branch = "py-structural", features = ["py_compatible"] }
[features]
debug = ["erg_compiler/debug", "erg_common/debug", "py2erg/debug"]

View file

@ -843,7 +843,8 @@ impl ASTConverter {
// self.y = y
// self.z = z
// ↓
// {x: Int, y: Int, z: Never}, .__call__(x: Int, y: Int, z: Obj): Self = .unreachable()
// requirement : {x: Int, y: Int, z: Never}
// returns : .__call__(x: Int, y: Int, z: Obj): Self = .unreachable()
fn extract_init(&mut self, base_type: &mut Option<Expr>, init_def: Def) -> Option<Def> {
self.check_init_sig(&init_def.sig)?;
let l_brace = Token::new(
@ -988,8 +989,8 @@ impl ASTConverter {
.map(|id| &id.inspect()[..] == "__init__")
.unwrap_or(false)
{
if let Some(init_def) = self.extract_init(&mut base_type, def) {
attrs.push(ClassAttr::Def(init_def));
if let Some(call_def) = self.extract_init(&mut base_type, def) {
attrs.insert(0, ClassAttr::Def(call_def));
init_is_defined = true;
}
} else {
@ -1041,7 +1042,7 @@ impl ASTConverter {
}
}
if !init_is_defined {
attrs.push(ClassAttr::Def(self.gen_default_init(0)));
attrs.insert(0, ClassAttr::Def(self.gen_default_init(0)));
}
(base_type, ClassAttrs::new(attrs))
}

View file

@ -11,19 +11,33 @@ class C:
def __init__(self, x: int, y): # y: Obj
self.x = x
self.y = y # y: Never
def __add__(self, other: C):
return C(self.x + other.x, self.y + other.y)
def method(self):
return self.x
c = C(1, 2)
assert c.x == 1
assert c.y == 2 # OK, c.y == "a" is also OK
# OK, c.y == "a" is also OK (cause the checker doesn't know the type of C.y)
assert c.y == 2
assert c.z == 3 # ERR
d = c + c
assert d.x == 2
assert d.x == "a" # ERR
a = c.method() # OK
_: int = a + 1
b = C("a").method() # ERR
class D:
c: int
def __add__(self, other: D):
return D(self.c + other.c)
def __sub__(self, other: C):
return D(self.c - other.x)
def __init__(self, c):
self.c = c
c1 = D(1).c + 1
d = D(1) + D(2)
err = C(1, 2) + D(1) # ERR
ok = D(1) - C(1, 2) # OK

View file

@ -1,6 +1,7 @@
use std::path::PathBuf;
use erg_common::config::{ErgConfig, Input};
use erg_common::spawn::exec_new_thread;
use erg_common::traits::Stream;
use erg_compiler::artifact::{CompleteArtifact, IncompleteArtifact};
use pylyzer::PythonAnalyzer;
@ -15,7 +16,8 @@ pub fn exec_analyzer(file_path: &'static str) -> Result<CompleteArtifact, Incomp
analyzer.analyze(py_code, "exec")
}
pub fn expect(file_path: &'static str, warns: usize, errors: usize) {
fn _expect(file_path: &'static str, warns: usize, errors: usize) {
println!("Testing {file_path} ...");
match exec_analyzer(file_path) {
Ok(artifact) => {
assert_eq!(artifact.warns.len(), warns);
@ -28,6 +30,12 @@ pub fn expect(file_path: &'static str, warns: usize, errors: usize) {
}
}
pub fn expect(file_path: &'static str, warns: usize, errors: usize) {
exec_new_thread(move || {
_expect(file_path, warns, errors);
});
}
#[test]
fn exec_test() {
expect("tests/test.py", 0, 10);
@ -50,7 +58,7 @@ fn exec_func() {
#[test]
fn exec_class() {
expect("tests/class.py", 0, 1);
expect("tests/class.py", 0, 4);
}
#[test]