fix: TypeVar bug

This commit is contained in:
Shunsuke Shibayama 2024-08-20 02:19:34 +09:00
parent 6e88efebe8
commit 94221a6419
6 changed files with 83 additions and 20 deletions

20
Cargo.lock generated
View File

@ -139,9 +139,9 @@ checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0"
[[package]] [[package]]
name = "els" name = "els"
version = "0.1.54-nightly.3" version = "0.1.54-nightly.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfe2679dec933507f4d9abfa4e6468d3b312790e933b3269ce2a2a2bc910bd3d" checksum = "bdc6282121d9e2871553e0731cfb88119cbfe76ce8aab08ac2be01f5644e3bee"
dependencies = [ dependencies = [
"erg_common", "erg_common",
"erg_compiler", "erg_compiler",
@ -153,9 +153,9 @@ dependencies = [
[[package]] [[package]]
name = "erg_common" name = "erg_common"
version = "0.6.42-nightly.3" version = "0.6.42-nightly.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c5582717e4cd56c2015263641441c7f3c19ae030bd804565f0b93ad0a8c536e" checksum = "c0dfc622cc65f230a05a284a21f62c8f4a3c964c51c97881cc4e01202ef2a3c0"
dependencies = [ dependencies = [
"backtrace-on-stack-overflow", "backtrace-on-stack-overflow",
"erg_proc_macros", "erg_proc_macros",
@ -165,9 +165,9 @@ dependencies = [
[[package]] [[package]]
name = "erg_compiler" name = "erg_compiler"
version = "0.6.42-nightly.3" version = "0.6.42-nightly.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "299406202ab6dfe28be95c3d6ceae19d9821624ef666a978b42112f658b528c4" checksum = "42247c4ab1eb33ed3e2e9e74f4773565eba4491f76bfbda4b965015938704ca1"
dependencies = [ dependencies = [
"erg_common", "erg_common",
"erg_parser", "erg_parser",
@ -175,9 +175,9 @@ dependencies = [
[[package]] [[package]]
name = "erg_parser" name = "erg_parser"
version = "0.6.42-nightly.3" version = "0.6.42-nightly.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cc13d9b7c3342e1a4ccd4159e39c99769a30733ac6cf44c22593d0aecb169af9" checksum = "6c921c178517c2071e45418e8c5b35e0c3a19021e4459283fd8f6997b89ba216"
dependencies = [ dependencies = [
"erg_common", "erg_common",
"erg_proc_macros", "erg_proc_macros",
@ -186,9 +186,9 @@ dependencies = [
[[package]] [[package]]
name = "erg_proc_macros" name = "erg_proc_macros"
version = "0.6.42-nightly.3" version = "0.6.42-nightly.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8506a5228f462df55923b4a6ca8617ce90a84b52a4b603cedf3dda6beb3243f" checksum = "c1381ca7a7a0781834cb1d617cd8361cca23f880cb9c515d249905d585832734"
dependencies = [ dependencies = [
"quote", "quote",
"syn 1.0.109", "syn 1.0.109",

View File

@ -22,9 +22,9 @@ edition = "2021"
repository = "https://github.com/mtshiba/pylyzer" repository = "https://github.com/mtshiba/pylyzer"
[workspace.dependencies] [workspace.dependencies]
erg_common = { version = "0.6.42-nightly.3", features = ["py_compat", "els"] } erg_common = { version = "0.6.42-nightly.5", features = ["py_compat", "els"] }
erg_compiler = { version = "0.6.42-nightly.3", features = ["py_compat", "els"] } erg_compiler = { version = "0.6.42-nightly.5", features = ["py_compat", "els"] }
els = { version = "0.1.54-nightly.3", features = ["py_compat"] } els = { version = "0.1.54-nightly.5", features = ["py_compat"] }
# rustpython-parser = { version = "0.3.0", features = ["all-nodes-with-ranges", "location"] } # 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-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"] } rustpython-parser = { git = "https://github.com/RustPython/Parser", version = "0.4.0", features = ["all-nodes-with-ranges", "location"] }

View File

@ -221,6 +221,7 @@ pub enum ShadowingMode {
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TypeVarInfo { pub struct TypeVarInfo {
name: String, name: String,
constraints: Vec<Expr>,
bound: Option<Expr>, bound: Option<Expr>,
} }
@ -235,8 +236,12 @@ impl fmt::Display for TypeVarInfo {
} }
impl TypeVarInfo { impl TypeVarInfo {
pub const fn new(name: String, bound: Option<Expr>) -> Self { pub const fn new(name: String, constraints: Vec<Expr>, bound: Option<Expr>) -> Self {
Self { name, bound } Self {
name,
constraints,
bound,
}
} }
} }
@ -1662,6 +1667,7 @@ impl ASTConverter {
fn get_type_bounds(&mut self, type_params: Vec<TypeParam>) -> TypeBoundSpecs { fn get_type_bounds(&mut self, type_params: Vec<TypeParam>) -> TypeBoundSpecs {
let mut bounds = TypeBoundSpecs::empty(); let mut bounds = TypeBoundSpecs::empty();
let mut errs = vec![];
if type_params.is_empty() { if type_params.is_empty() {
for ty in self.cur_appeared_type_names() { for ty in self.cur_appeared_type_names() {
let name = VarName::from_str(ty.clone().into()); let name = VarName::from_str(ty.clone().into());
@ -1672,6 +1678,42 @@ impl ASTConverter {
.unwrap_or(TypeSpec::Infer(name.token().clone())); .unwrap_or(TypeSpec::Infer(name.token().clone()));
let spec = TypeSpecWithOp::new(op, t_spec, bound.clone()); let spec = TypeSpecWithOp::new(op, t_spec, bound.clone());
TypeBoundSpec::non_default(name, spec) TypeBoundSpec::non_default(name, spec)
} else if !tv_info.constraints.is_empty() {
if tv_info.constraints.len() == 1 {
let err = CompileError::syntax_error(
self.cfg.input.clone(),
line!() as usize,
pyloc_to_ergloc(type_params[0].range()),
self.cur_namespace(),
"TypeVar must have at least two constrained types".into(),
None,
);
errs.push(err);
}
let op = Token::dummy(TokenKind::Colon, ":");
let mut elems = vec![];
for constraint in tv_info.constraints.iter() {
if let Ok(expr) = Parser::validate_const_expr(constraint.clone()) {
elems.push(ConstPosArg::new(expr));
}
}
let t_spec = TypeSpec::Enum(ConstArgs::pos_only(elems, None));
let elems = Args::pos_only(
tv_info
.constraints
.iter()
.cloned()
.map(PosArg::new)
.collect(),
None,
);
let expr = Expr::Set(Set::Normal(NormalSet::new(
Token::DUMMY,
Token::DUMMY,
elems,
)));
let spec = TypeSpecWithOp::new(op, t_spec, expr);
TypeBoundSpec::non_default(name, spec)
} else { } else {
TypeBoundSpec::Omitted(name) TypeBoundSpec::Omitted(name)
}; };
@ -1696,6 +1738,7 @@ impl ASTConverter {
}; };
bounds.push(spec); bounds.push(spec);
} }
self.errs.extend(errs);
bounds bounds
} }
@ -1893,8 +1936,14 @@ impl ASTConverter {
} else { } else {
name.id.to_string() name.id.to_string()
}; };
let bound = call.args.nth_or_key(1, "bound").cloned(); let mut constraints = vec![];
let info = TypeVarInfo::new(arg, bound); let mut nth = 1;
while let Some(constr) = call.args.get_nth(nth) {
constraints.push(constr.clone());
nth += 1;
}
let bound = call.args.get_with_key("bound").cloned();
let info = TypeVarInfo::new(arg, constraints, bound);
self.define_type_var(name.id.to_string(), info); self.define_type_var(name.id.to_string(), info);
} }
} }

View File

@ -71,3 +71,10 @@ class F:
_ = F(1) _ = F(1)
_ = F(1, 2) _ = F(1, 2)
_ = F(1, z=1, y=2) _ = F(1, z=1, y=2)
class G(DoesNotExist): # ERR
def foo(self):
return 1
g = G()
assert g.foo() == 1

View File

@ -84,7 +84,7 @@ fn exec_func() -> Result<(), String> {
#[test] #[test]
fn exec_class() -> Result<(), String> { fn exec_class() -> Result<(), String> {
expect("tests/class.py", 0, 5) expect("tests/class.py", 0, 6)
} }
#[test] #[test]
@ -139,7 +139,7 @@ fn exec_shadowing() -> Result<(), String> {
#[test] #[test]
fn exec_typevar() -> Result<(), String> { fn exec_typevar() -> Result<(), String> {
expect("tests/typevar.py", 0, 2) expect("tests/typevar.py", 0, 3)
} }
#[test] #[test]

View File

@ -1,17 +1,24 @@
from typing import TypeVar from typing import TypeVar
T = TypeVar("T") T = TypeVar("T")
U = TypeVar("U", int) U = TypeVar("U", bound=int)
IS = TypeVar("IS", int, str)
def id(x: T) -> T: def id(x: T) -> T:
return x return x
def id_int(x: U) -> U: def id_int(x: U) -> U:
return x return x
def id_int_or_str(x: IS) -> IS:
return x
_ = id(1) + 1 # OK _ = id(1) + 1 # OK
_ = id("a") + "b" # OK _ = id("a") + "b" # OK
_ = id_int(1) # OK _ = id_int(1) # OK
_ = id_int("a") # ERR _ = id_int("a") # ERR
_ = id_int_or_str(1) # OK
_ = id_int_or_str("a") # OK
_ = id_int_or_str(None) # ERR
def id2[T](x: T) -> T: def id2[T](x: T) -> T:
return x return x