mirror of
https://github.com/erg-lang/erg.git
synced 2025-09-28 04:09:05 +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() => {
|
Expr::Accessor(Accessor::Ident(ident)) if ident.vis().is_private() => {
|
||||||
self.emit_call_local(ident, call.args)
|
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 => {
|
other => {
|
||||||
let is_py_api = other.is_py_api();
|
let is_py_api = other.is_py_api();
|
||||||
self.emit_push_null();
|
self.emit_push_null();
|
||||||
|
@ -2337,6 +2341,10 @@ impl PyCodeGenerator {
|
||||||
Some(7) => self.emit_with_instr_307(args),
|
Some(7) => self.emit_with_instr_307(args),
|
||||||
_ => todo!("not supported Python version"),
|
_ => 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
|
// "pyimport" | "py" are here
|
||||||
_ => {
|
_ => {
|
||||||
let is_py_api = local.is_py_api();
|
let is_py_api = local.is_py_api();
|
||||||
|
@ -2369,11 +2377,16 @@ impl PyCodeGenerator {
|
||||||
if let Some(func_name) = debind(&method_name) {
|
if let Some(func_name) = debind(&method_name) {
|
||||||
return self.emit_call_fake_method(obj, func_name, method_name, args);
|
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();
|
let is_py_api = method_name.is_py_api();
|
||||||
self.emit_expr(obj);
|
self.emit_expr(obj);
|
||||||
self.emit_load_method_instr(method_name);
|
self.emit_load_method_instr(method_name);
|
||||||
|
if is_type {
|
||||||
|
self.emit_index_args(args);
|
||||||
|
} else {
|
||||||
self.emit_args_311(args, BoundAttr, is_py_api);
|
self.emit_args_311(args, BoundAttr, is_py_api);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn emit_var_args_311(&mut self, pos_len: usize, var_args: &PosArg) {
|
fn emit_var_args_311(&mut self, pos_len: usize, var_args: &PosArg) {
|
||||||
if pos_len > 0 {
|
if pos_len > 0 {
|
||||||
|
@ -2449,6 +2462,24 @@ impl PyCodeGenerator {
|
||||||
self.stack_dec_n((1 + argc + kwsc) - 1);
|
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.update! x -> x + 1
|
||||||
/// => X = mutate_operator((x -> x + 1)(X))
|
/// => X = mutate_operator((x -> x + 1)(X))
|
||||||
/// TODO: should be `X = X + 1` in the above case
|
/// TODO: should be `X = X + 1` in the above case
|
||||||
|
|
|
@ -999,6 +999,13 @@ impl Context {
|
||||||
type_.register_superclass(Obj, &obj);
|
type_.register_superclass(Obj, &obj);
|
||||||
type_.register_builtin_erg_impl(
|
type_.register_builtin_erg_impl(
|
||||||
FUNC_MRO,
|
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)),
|
array_t(Type, TyParam::erased(Nat)),
|
||||||
Immutable,
|
Immutable,
|
||||||
Visibility::BUILTIN_PUBLIC,
|
Visibility::BUILTIN_PUBLIC,
|
||||||
|
|
|
@ -402,6 +402,7 @@ const OP_MUTATE: &str = "__mutate__";
|
||||||
const OP_POS: &str = "__pos__";
|
const OP_POS: &str = "__pos__";
|
||||||
const OP_NEG: &str = "__neg__";
|
const OP_NEG: &str = "__neg__";
|
||||||
|
|
||||||
|
const FUNDAMENTAL_ARGS: &str = "__args__";
|
||||||
const FUNDAMENTAL_LEN: &str = "__len__";
|
const FUNDAMENTAL_LEN: &str = "__len__";
|
||||||
const FUNDAMENTAL_CONTAINS: &str = "__contains__";
|
const FUNDAMENTAL_CONTAINS: &str = "__contains__";
|
||||||
const FUNDAMENTAL_CALL: &str = "__call__";
|
const FUNDAMENTAL_CALL: &str = "__call__";
|
||||||
|
|
|
@ -2,6 +2,7 @@ from _erg_control import then__
|
||||||
from _erg_range import Range
|
from _erg_range import Range
|
||||||
from _erg_nat import NatMut
|
from _erg_nat import NatMut
|
||||||
from _erg_int import IntMut
|
from _erg_int import IntMut
|
||||||
|
from _erg_contains_operator import contains_operator
|
||||||
|
|
||||||
class Array(list):
|
class Array(list):
|
||||||
def dedup(self, same_bucket=None):
|
def dedup(self, same_bucket=None):
|
||||||
|
@ -43,3 +44,22 @@ class Array(list):
|
||||||
return Array(list.__getitem__(self, index_or_slice.into_slice()))
|
return Array(list.__getitem__(self, index_or_slice.into_slice()))
|
||||||
else:
|
else:
|
||||||
return list.__getitem__(self, index_or_slice)
|
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)
|
# (elem in y) == contains_operator(y, elem)
|
||||||
def contains_operator(y, elem):
|
def contains_operator(y, elem):
|
||||||
|
if hasattr(elem, "type_check"):
|
||||||
|
return elem.type_check(y)
|
||||||
# 1 in Int
|
# 1 in Int
|
||||||
if type(y) == type:
|
elif type(y) == type:
|
||||||
if isinstance(elem, y):
|
if isinstance(elem, y):
|
||||||
return True
|
return True
|
||||||
elif hasattr(y, "try_new") and is_ok(y.try_new(elem)):
|
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]
|
def try_new(i): # -> Result[Nat]
|
||||||
if isinstance(i, float):
|
if isinstance(i, float):
|
||||||
Float(i)
|
return Float(i)
|
||||||
else:
|
else:
|
||||||
Error("not a float")
|
return Error("not a float")
|
||||||
|
|
||||||
def mutate(self):
|
def mutate(self):
|
||||||
return FloatMut(self)
|
return FloatMut(self)
|
||||||
|
|
|
@ -5,9 +5,9 @@ from _erg_control import then__
|
||||||
class Int(int):
|
class Int(int):
|
||||||
def try_new(i): # -> Result[Nat]
|
def try_new(i): # -> Result[Nat]
|
||||||
if isinstance(i, int):
|
if isinstance(i, int):
|
||||||
Int(i)
|
return Int(i)
|
||||||
else:
|
else:
|
||||||
Error("not an integer")
|
return Error("not an integer")
|
||||||
|
|
||||||
def succ(self):
|
def succ(self):
|
||||||
return Int(self + 1)
|
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> {
|
pub fn as_free(&self) -> Option<&FreeTyVar> {
|
||||||
<&FreeTyVar>::try_from(self).ok()
|
<&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)
|
* `COMPARE_OP` (6 byte)
|
||||||
* `LOAD_GLOBAL` (12 byte)
|
* `LOAD_GLOBAL` (12 byte)
|
||||||
* `LOAD_ATTR` (10 byte)
|
* `LOAD_ATTR` (10 byte)
|
||||||
|
* `BINARY_SUBSCR` (8 byte)
|
||||||
|
|
||||||
## STORE_NAME(namei)
|
## STORE_NAME(namei)
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ Python bytecodeの変数操作系の命令はnamei (name index)を通してア
|
||||||
* `COMPARE_OP` (6 byte)
|
* `COMPARE_OP` (6 byte)
|
||||||
* `LOAD_GLOBAL` (12 byte)
|
* `LOAD_GLOBAL` (12 byte)
|
||||||
* `LOAD_ATTR` (10 byte)
|
* `LOAD_ATTR` (10 byte)
|
||||||
|
* `BINARY_SUBSCR` (8 byte)
|
||||||
|
|
||||||
## STORE_NAME(namei)
|
## 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)
|
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]
|
#[test]
|
||||||
fn exec_class() -> Result<(), ()> {
|
fn exec_class() -> Result<(), ()> {
|
||||||
expect_success("examples/class.er", 0)
|
expect_success("examples/class.er", 0)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue