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

View File

@ -22,9 +22,9 @@ edition = "2021"
repository = "https://github.com/mtshiba/pylyzer"
[workspace.dependencies]
erg_common = { version = "0.6.42-nightly.3", features = ["py_compat", "els"] }
erg_compiler = { version = "0.6.42-nightly.3", features = ["py_compat", "els"] }
els = { version = "0.1.54-nightly.3", features = ["py_compat"] }
erg_common = { version = "0.6.42-nightly.5", features = ["py_compat", "els"] }
erg_compiler = { version = "0.6.42-nightly.5", features = ["py_compat", "els"] }
els = { version = "0.1.54-nightly.5", 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"] }

View File

@ -221,6 +221,7 @@ pub enum ShadowingMode {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TypeVarInfo {
name: String,
constraints: Vec<Expr>,
bound: Option<Expr>,
}
@ -235,8 +236,12 @@ impl fmt::Display for TypeVarInfo {
}
impl TypeVarInfo {
pub const fn new(name: String, bound: Option<Expr>) -> Self {
Self { name, bound }
pub const fn new(name: String, constraints: Vec<Expr>, bound: Option<Expr>) -> Self {
Self {
name,
constraints,
bound,
}
}
}
@ -1662,6 +1667,7 @@ impl ASTConverter {
fn get_type_bounds(&mut self, type_params: Vec<TypeParam>) -> TypeBoundSpecs {
let mut bounds = TypeBoundSpecs::empty();
let mut errs = vec![];
if type_params.is_empty() {
for ty in self.cur_appeared_type_names() {
let name = VarName::from_str(ty.clone().into());
@ -1672,6 +1678,42 @@ impl ASTConverter {
.unwrap_or(TypeSpec::Infer(name.token().clone()));
let spec = TypeSpecWithOp::new(op, t_spec, bound.clone());
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 {
TypeBoundSpec::Omitted(name)
};
@ -1696,6 +1738,7 @@ impl ASTConverter {
};
bounds.push(spec);
}
self.errs.extend(errs);
bounds
}
@ -1893,8 +1936,14 @@ impl ASTConverter {
} else {
name.id.to_string()
};
let bound = call.args.nth_or_key(1, "bound").cloned();
let info = TypeVarInfo::new(arg, bound);
let mut constraints = vec![];
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);
}
}

View File

@ -71,3 +71,10 @@ class F:
_ = F(1)
_ = F(1, 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]
fn exec_class() -> Result<(), String> {
expect("tests/class.py", 0, 5)
expect("tests/class.py", 0, 6)
}
#[test]
@ -139,7 +139,7 @@ fn exec_shadowing() -> Result<(), String> {
#[test]
fn exec_typevar() -> Result<(), String> {
expect("tests/typevar.py", 0, 2)
expect("tests/typevar.py", 0, 3)
}
#[test]

View File

@ -1,17 +1,24 @@
from typing import TypeVar
T = TypeVar("T")
U = TypeVar("U", int)
U = TypeVar("U", bound=int)
IS = TypeVar("IS", int, str)
def id(x: T) -> T:
return x
def id_int(x: U) -> U:
return x
def id_int_or_str(x: IS) -> IS:
return x
_ = id(1) + 1 # OK
_ = id("a") + "b" # OK
_ = id_int(1) # OK
_ = 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:
return x