Skip to content

Commit

Permalink
fix: type inference bugs
Browse files Browse the repository at this point in the history
  • Loading branch information
mtshiba committed Dec 29, 2024
1 parent faec281 commit 9cd1846
Show file tree
Hide file tree
Showing 8 changed files with 83 additions and 24 deletions.
20 changes: 10 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ edition = "2021"
repository = "https://github.com/mtshiba/pylyzer"

[workspace.dependencies]
erg_common = { version = "0.6.50", features = ["py_compat", "els"] }
erg_compiler = { version = "0.6.50", features = ["py_compat", "els"] }
els = { version = "0.1.50", features = ["py_compat"] }
erg_common = { version = "0.6.51-nightly.0", features = ["py_compat", "els"] }
erg_compiler = { version = "0.6.51-nightly.0", features = ["py_compat", "els"] }
els = { version = "0.1.63-nightly.0", features = ["py_compat"] }
# rustpython-parser = { version = "0.3.0", features = ["all-nodes-with-ranges", "location"] }
# rustpython-ast = { version = "0.3.0", features = ["all-nodes-with-ranges", "location"] }
rustpython-parser = { git = "https://github.com/RustPython/Parser", version = "0.4.0", features = ["all-nodes-with-ranges", "location"] }
Expand Down
52 changes: 47 additions & 5 deletions crates/py2erg/convert.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,19 @@ pub struct BlockInfo {
pub kind: BlockKind,
}

#[derive(Debug)]
pub enum ReturnKind {
None,
Return,
Yield,
}

impl ReturnKind {
pub const fn is_none(&self) -> bool {
matches!(self, Self::None)
}
}

#[derive(Debug)]
pub struct LocalContext {
pub name: String,
Expand All @@ -329,6 +342,7 @@ pub struct LocalContext {
type_vars: HashMap<String, TypeVarInfo>,
// e.g. def id(x: T) -> T: ... => appeared_types = {T}
appeared_type_names: HashSet<String>,
return_kind: ReturnKind,
}

impl LocalContext {
Expand All @@ -339,6 +353,7 @@ impl LocalContext {
names: HashMap::new(),
type_vars: HashMap::new(),
appeared_type_names: HashSet::new(),
return_kind: ReturnKind::None,
}
}
}
Expand Down Expand Up @@ -667,6 +682,14 @@ impl ASTConverter {
&self.contexts.last().unwrap().name
}

fn cur_context(&self) -> &LocalContext {
self.contexts.last().unwrap()
}

fn cur_context_mut(&mut self) -> &mut LocalContext {
self.contexts.last_mut().unwrap()
}

fn parent_name(&self) -> &str {
&self.contexts[self.contexts.len().saturating_sub(2)].name
}
Expand Down Expand Up @@ -2033,6 +2056,11 @@ impl ASTConverter {
);
Expr::Compound(Compound::new(vec![Expr::Def(def), target]))
}
py_ast::Expr::Yield(_) => {
self.cur_context_mut().return_kind = ReturnKind::Yield;
log!(err "unimplemented: {:?}", expr);
Expr::Dummy(Dummy::new(None, vec![]))
}
_other => {
log!(err "unimplemented: {:?}", _other);
Expr::Dummy(Dummy::new(None, vec![]))
Expand Down Expand Up @@ -2087,7 +2115,7 @@ impl ASTConverter {
// self.y = y
// self.z = z
// ↓
// requirement : {x: Int, y: Int, z: Never}
// requirement : {x: Int, y: Int, z: Any}
// 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)?;
Expand Down Expand Up @@ -2134,9 +2162,8 @@ impl ASTConverter {
} else if let Some(typ) = redef.t_spec.map(|t_spec| t_spec.t_spec_as_expr) {
*typ
} else {
Expr::from(Accessor::Ident(Identifier::public_with_line(
DOT,
"Never".into(),
Expr::from(Accessor::Ident(Identifier::private_with_line(
"Any".into(),
attr.obj.ln_begin().unwrap_or(0),
)))
};
Expand Down Expand Up @@ -2473,8 +2500,22 @@ impl ASTConverter {
.unwrap_or(func_def.type_params)
};
let bounds = self.get_type_bounds(type_params);
let sig = Signature::Subr(SubrSignature::new(decos, ident, bounds, params, return_t));
let mut sig =
Signature::Subr(SubrSignature::new(decos, ident, bounds, params, return_t));
let block = self.convert_block(func_def.body, BlockKind::Function);
if self.cur_context().return_kind.is_none() {
let Signature::Subr(subr) = &mut sig else {
unreachable!()
};
if subr.return_t_spec.is_none() {
let none = TypeSpecWithOp::new(
Token::dummy(TokenKind::Colon, ":"),
TypeSpec::mono(Identifier::private("NoneType".into())),
Expr::static_local("NoneType"),
);
subr.return_t_spec = Some(Box::new(none));
}
}
let body = DefBody::new(EQUAL, block, DefId(0));
let def = Def::new(sig, body);
self.pop();
Expand Down Expand Up @@ -3030,6 +3071,7 @@ impl ASTConverter {
}
}
py_ast::Stmt::Return(return_) => {
self.cur_context_mut().return_kind = ReturnKind::Return;
let loc = return_.location();
let value = return_
.value
Expand Down
9 changes: 5 additions & 4 deletions crates/pylyzer_core/handle_err.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,11 @@ fn handle_name_error(error: CompileError) -> Option<CompileError> {
|| {
main.contains(" is not defined") && {
let name = StyledStr::destyle(main.trim_end_matches(" is not defined"));
error
.core
.get_hint()
.is_some_and(|hint| hint.contains(name))
name == "Any"
|| error
.core
.get_hint()
.is_some_and(|hint| hint.contains(name))
}
}
{
Expand Down
7 changes: 7 additions & 0 deletions tests/class.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,3 +98,10 @@ def try_new(lis) -> "MyList" | None:
return MyList(lis)
else:
return None

class Implicit:
def __init__(self):
self.foo = False

def set_foo(self):
self.foo = True
4 changes: 4 additions & 0 deletions tests/err/class.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
class Foo:
def invalid_append(self):
paths: list[str] = []
paths.append(self) # ERR
4 changes: 2 additions & 2 deletions tests/projection.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
def imaginary(x):
x.imag
return x.imag

assert imaginary(1) == 0
assert imaginary(1.0) <= 0.0
Expand All @@ -8,7 +8,7 @@ def imaginary(x):
class C:
def method(self, x): return x
def call_method(obj, x):
obj.method(x)
return obj.method(x)

c = C()
assert call_method(c, 1) == 1
Expand Down
5 changes: 5 additions & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,11 @@ fn exec_class() -> Result<(), String> {
expect("tests/class.py", 0, 6)
}

#[test]
fn exec_class_err() -> Result<(), String> {
expect("tests/err/class.py", 0, 1)
}

#[test]
fn exec_errors() -> Result<(), String> {
expect("tests/errors.py", 0, 3)
Expand Down

0 comments on commit 9cd1846

Please sign in to comment.