mirror of
https://github.com/erg-lang/erg.git
synced 2025-09-27 19:59:07 +00:00
fix: dynamic type checking bugs
This commit is contained in:
parent
a6b72ea636
commit
5affa5065f
12 changed files with 91 additions and 7 deletions
|
@ -2307,6 +2307,10 @@ impl PyCodeGenerator {
|
|||
Expr::Accessor(Accessor::Ident(ident)) if ident.vis().is_private() => {
|
||||
self.emit_call_local(ident, call.args)
|
||||
}
|
||||
other if other.ref_t().is_type() => {
|
||||
self.emit_expr(other);
|
||||
self.emit_index_args(call.args);
|
||||
}
|
||||
other => {
|
||||
let is_py_api = other.is_py_api();
|
||||
self.emit_push_null();
|
||||
|
@ -2337,6 +2341,10 @@ impl PyCodeGenerator {
|
|||
Some(7) => self.emit_with_instr_307(args),
|
||||
_ => todo!("not supported Python version"),
|
||||
},
|
||||
_ if local.ref_t().is_type() => {
|
||||
self.emit_load_name_instr(local);
|
||||
self.emit_index_args(args);
|
||||
}
|
||||
// "pyimport" | "py" are here
|
||||
_ => {
|
||||
let is_py_api = local.is_py_api();
|
||||
|
@ -2369,10 +2377,15 @@ impl PyCodeGenerator {
|
|||
if let Some(func_name) = debind(&method_name) {
|
||||
return self.emit_call_fake_method(obj, func_name, method_name, args);
|
||||
}
|
||||
let is_type = method_name.ref_t().is_type();
|
||||
let is_py_api = method_name.is_py_api();
|
||||
self.emit_expr(obj);
|
||||
self.emit_load_method_instr(method_name);
|
||||
self.emit_args_311(args, BoundAttr, is_py_api);
|
||||
if is_type {
|
||||
self.emit_index_args(args);
|
||||
} else {
|
||||
self.emit_args_311(args, BoundAttr, is_py_api);
|
||||
}
|
||||
}
|
||||
|
||||
fn emit_var_args_311(&mut self, pos_len: usize, var_args: &PosArg) {
|
||||
|
@ -2449,6 +2462,24 @@ impl PyCodeGenerator {
|
|||
self.stack_dec_n((1 + argc + kwsc) - 1);
|
||||
}
|
||||
|
||||
fn emit_index_args(&mut self, mut args: Args) {
|
||||
let argc = args.pos_args.len();
|
||||
while let Some(arg) = args.try_remove_pos(0) {
|
||||
self.emit_expr(arg.expr);
|
||||
}
|
||||
if argc > 1 {
|
||||
self.write_instr(BUILD_TUPLE);
|
||||
self.write_arg(argc);
|
||||
}
|
||||
self.write_instr(Opcode311::BINARY_SUBSCR);
|
||||
self.write_arg(0);
|
||||
if self.py_version.minor >= Some(11) {
|
||||
self.write_bytes(&[0; 8]);
|
||||
}
|
||||
// (1 (subroutine) + argc) input objects -> 1 return object
|
||||
self.stack_dec_n((1 + argc) - 1);
|
||||
}
|
||||
|
||||
/// X.update! x -> x + 1
|
||||
/// => X = mutate_operator((x -> x + 1)(X))
|
||||
/// TODO: should be `X = X + 1` in the above case
|
||||
|
|
|
@ -999,6 +999,13 @@ impl Context {
|
|||
type_.register_superclass(Obj, &obj);
|
||||
type_.register_builtin_erg_impl(
|
||||
FUNC_MRO,
|
||||
fn0_met(Type, array_t(Type, TyParam::erased(Nat))),
|
||||
Immutable,
|
||||
Visibility::BUILTIN_PUBLIC,
|
||||
);
|
||||
// TODO: PolyType
|
||||
type_.register_builtin_erg_impl(
|
||||
FUNDAMENTAL_ARGS,
|
||||
array_t(Type, TyParam::erased(Nat)),
|
||||
Immutable,
|
||||
Visibility::BUILTIN_PUBLIC,
|
||||
|
|
|
@ -402,6 +402,7 @@ const OP_MUTATE: &str = "__mutate__";
|
|||
const OP_POS: &str = "__pos__";
|
||||
const OP_NEG: &str = "__neg__";
|
||||
|
||||
const FUNDAMENTAL_ARGS: &str = "__args__";
|
||||
const FUNDAMENTAL_LEN: &str = "__len__";
|
||||
const FUNDAMENTAL_CONTAINS: &str = "__contains__";
|
||||
const FUNDAMENTAL_CALL: &str = "__call__";
|
||||
|
|
|
@ -2,6 +2,7 @@ from _erg_control import then__
|
|||
from _erg_range import Range
|
||||
from _erg_nat import NatMut
|
||||
from _erg_int import IntMut
|
||||
from _erg_contains_operator import contains_operator
|
||||
|
||||
class Array(list):
|
||||
def dedup(self, same_bucket=None):
|
||||
|
@ -43,3 +44,22 @@ class Array(list):
|
|||
return Array(list.__getitem__(self, index_or_slice.into_slice()))
|
||||
else:
|
||||
return list.__getitem__(self, index_or_slice)
|
||||
|
||||
def type_check(self, t: type) -> bool:
|
||||
if isinstance(t, list):
|
||||
if len(t) != len(self):
|
||||
return False
|
||||
for (inner_t, elem) in zip(t, self):
|
||||
if not contains_operator(inner_t, elem):
|
||||
return False
|
||||
return True
|
||||
elif not hasattr(t, "__args__"):
|
||||
return isinstance(self, t)
|
||||
elem_t = t.__args__[0]
|
||||
l = None if len(t.__args__) != 2 else t.__args__[1]
|
||||
if l is not None and l != len(self):
|
||||
return False
|
||||
for elem in self:
|
||||
if not contains_operator(elem_t, elem):
|
||||
return False
|
||||
return True
|
||||
|
|
|
@ -5,8 +5,10 @@ from collections import namedtuple
|
|||
|
||||
# (elem in y) == contains_operator(y, elem)
|
||||
def contains_operator(y, elem):
|
||||
if hasattr(elem, "type_check"):
|
||||
return elem.type_check(y)
|
||||
# 1 in Int
|
||||
if type(y) == type:
|
||||
elif type(y) == type:
|
||||
if isinstance(elem, y):
|
||||
return True
|
||||
elif hasattr(y, "try_new") and is_ok(y.try_new(elem)):
|
||||
|
|
|
@ -7,9 +7,9 @@ class Float(float):
|
|||
|
||||
def try_new(i): # -> Result[Nat]
|
||||
if isinstance(i, float):
|
||||
Float(i)
|
||||
return Float(i)
|
||||
else:
|
||||
Error("not a float")
|
||||
return Error("not a float")
|
||||
|
||||
def mutate(self):
|
||||
return FloatMut(self)
|
||||
|
|
|
@ -5,9 +5,9 @@ from _erg_control import then__
|
|||
class Int(int):
|
||||
def try_new(i): # -> Result[Nat]
|
||||
if isinstance(i, int):
|
||||
Int(i)
|
||||
return Int(i)
|
||||
else:
|
||||
Error("not an integer")
|
||||
return Error("not an integer")
|
||||
|
||||
def succ(self):
|
||||
return Int(self + 1)
|
||||
|
|
|
@ -2029,6 +2029,17 @@ impl Type {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn is_type(&self) -> bool {
|
||||
match self {
|
||||
Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_type(),
|
||||
Self::Refinement(refine) => refine.t.is_type(),
|
||||
Self::ClassType | Self::TraitType | Self::Type => true,
|
||||
Self::Quantified(q) => q.is_type(),
|
||||
Self::Subr(subr) => subr.return_t.is_type(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_free(&self) -> Option<&FreeTyVar> {
|
||||
<&FreeTyVar>::try_from(self).ok()
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ Instructions that do not take arguments also use 2 bytes (the argument part is 0
|
|||
* `COMPARE_OP` (6 byte)
|
||||
* `LOAD_GLOBAL` (12 byte)
|
||||
* `LOAD_ATTR` (10 byte)
|
||||
* `BINARY_SUBSCR` (8 byte)
|
||||
|
||||
## STORE_NAME(namei)
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ Python bytecodeの変数操作系の命令はnamei (name index)を通してア
|
|||
* `COMPARE_OP` (6 byte)
|
||||
* `LOAD_GLOBAL` (12 byte)
|
||||
* `LOAD_ATTR` (10 byte)
|
||||
* `BINARY_SUBSCR` (8 byte)
|
||||
|
||||
## STORE_NAME(namei)
|
||||
|
||||
|
|
5
tests/should_ok/assert_cast.er
Normal file
5
tests/should_ok/assert_cast.er
Normal file
|
@ -0,0 +1,5 @@
|
|||
arr = ["a"]
|
||||
assert arr in Array(Str)
|
||||
assert arr in Array(Str, 1)
|
||||
assert arr notin Array(Int)
|
||||
assert arr notin Array(Str, 2)
|
|
@ -22,6 +22,11 @@ fn exec_array_member() -> Result<(), ()> {
|
|||
expect_success("tests/should_ok/array_member.er", 0)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_assert_cast_ok() -> Result<(), ()> {
|
||||
expect_success("tests/should_ok/assert_cast.er", 0)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_class() -> Result<(), ()> {
|
||||
expect_success("examples/class.er", 0)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue