mirror of
https://github.com/erg-lang/erg.git
synced 2025-08-04 18:58:30 +00:00
fix: bugs & remove TypeCmpCache
This commit is contained in:
parent
91fd285ccd
commit
66cac2ccc2
15 changed files with 68 additions and 118 deletions
|
@ -1,67 +0,0 @@
|
|||
use std::borrow::Borrow;
|
||||
use std::cell::RefCell;
|
||||
use std::hash::Hash;
|
||||
use std::thread::LocalKey;
|
||||
|
||||
use erg_common::dict::Dict;
|
||||
|
||||
use crate::ty::Type;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct SubtypePair {
|
||||
pub sub: Type,
|
||||
pub sup: Type,
|
||||
}
|
||||
|
||||
impl SubtypePair {
|
||||
pub const fn new(sub: Type, sup: Type) -> Self {
|
||||
Self { sub, sup }
|
||||
}
|
||||
}
|
||||
|
||||
/// Caches type relationships.
|
||||
/// The cost of searching for subtype relations of a class, for example, is not small.
|
||||
/// Some relationships are cached because they tend to be queried many times.
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TypeCmpCache {
|
||||
cache: Dict<SubtypePair, bool>,
|
||||
}
|
||||
|
||||
impl TypeCmpCache {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
pub fn get<Q: Eq + Hash>(&self, pair: &Q) -> Option<bool>
|
||||
where
|
||||
SubtypePair: Borrow<Q>,
|
||||
{
|
||||
self.cache.get(pair).copied()
|
||||
}
|
||||
|
||||
pub fn register(&mut self, pair: SubtypePair, b: bool) {
|
||||
self.cache.insert(pair, b);
|
||||
}
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static TYPE_CACHE: RefCell<TypeCmpCache> = RefCell::new(TypeCmpCache::default());
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct GlobalTypeCmpCache(LocalKey<RefCell<TypeCmpCache>>);
|
||||
|
||||
pub static GLOBAL_TYPE_CACHE: GlobalTypeCmpCache = GlobalTypeCmpCache(TYPE_CACHE);
|
||||
|
||||
impl GlobalTypeCmpCache {
|
||||
pub fn get<Q: Eq + Hash>(&'static self, pair: &Q) -> Option<bool>
|
||||
where
|
||||
SubtypePair: Borrow<Q>,
|
||||
{
|
||||
self.0.with(|s| s.borrow().get(pair))
|
||||
}
|
||||
|
||||
pub fn register(&'static self, pair: SubtypePair, b: bool) {
|
||||
self.0.with(|s| s.borrow_mut().register(pair, b));
|
||||
}
|
||||
}
|
|
@ -19,7 +19,6 @@ use Predicate as Pred;
|
|||
use TyParamOrdering::*;
|
||||
use Type::*;
|
||||
|
||||
use crate::context::cache::{SubtypePair, GLOBAL_TYPE_CACHE};
|
||||
use crate::context::{Context, Variance};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
|
@ -33,19 +32,6 @@ use Credibility::*;
|
|||
use super::ContextKind;
|
||||
|
||||
impl Context {
|
||||
fn register_cache(&self, sup: &Type, sub: &Type, result: bool) {
|
||||
GLOBAL_TYPE_CACHE.register(SubtypePair::new(sub.clone(), sup.clone()), result);
|
||||
}
|
||||
|
||||
// TODO: is it impossible to avoid .clone()?
|
||||
fn inquire_cache(&self, sup: &Type, sub: &Type) -> Option<bool> {
|
||||
let res = GLOBAL_TYPE_CACHE.get(&SubtypePair::new(sub.clone(), sup.clone()));
|
||||
if res.is_some() {
|
||||
log!(info "cache hit");
|
||||
}
|
||||
res
|
||||
}
|
||||
|
||||
pub(crate) fn eq_tp(&self, lhs: &TyParam, rhs: &TyParam) -> bool {
|
||||
match (lhs, rhs) {
|
||||
(TyParam::Type(lhs), TyParam::Type(rhs))
|
||||
|
@ -230,18 +216,12 @@ impl Context {
|
|||
/// make judgments that include supertypes in the same namespace & take into account glue patches
|
||||
/// 同一名前空間にある上位型を含めた判定&接着パッチを考慮した判定を行う
|
||||
fn nominal_supertype_of(&self, lhs: &Type, rhs: &Type) -> bool {
|
||||
if let Some(res) = self.inquire_cache(lhs, rhs) {
|
||||
return res;
|
||||
}
|
||||
if let (Absolutely, judge) = self.classes_supertype_of(lhs, rhs) {
|
||||
self.register_cache(lhs, rhs, judge);
|
||||
return judge;
|
||||
}
|
||||
if let (Absolutely, judge) = self.traits_supertype_of(lhs, rhs) {
|
||||
self.register_cache(lhs, rhs, judge);
|
||||
return judge;
|
||||
}
|
||||
self.register_cache(lhs, rhs, false);
|
||||
false
|
||||
}
|
||||
|
||||
|
@ -768,6 +748,10 @@ impl Context {
|
|||
.all(|((lp, rp), variance)| self.supertype_of_tp(lp, rp, *variance))
|
||||
}
|
||||
|
||||
fn _subtype_of_tp(&self, lp: &TyParam, rp: &TyParam, variance: Variance) -> bool {
|
||||
self.supertype_of_tp(rp, lp, variance)
|
||||
}
|
||||
|
||||
fn supertype_of_tp(&self, lp: &TyParam, rp: &TyParam, variance: Variance) -> bool {
|
||||
if lp == rp {
|
||||
return true;
|
||||
|
@ -779,12 +763,22 @@ impl Context {
|
|||
(_, TyParam::FreeVar(fv)) if fv.is_linked() => {
|
||||
self.supertype_of_tp(lp, &fv.crack(), variance)
|
||||
}
|
||||
// _: Type :> T == true
|
||||
(TyParam::Erased(t), TyParam::Type(_)) | (TyParam::Type(_), TyParam::Erased(t))
|
||||
if t.as_ref() == &Type =>
|
||||
{
|
||||
true
|
||||
}
|
||||
(TyParam::Erased(t), _) => match variance {
|
||||
Variance::Contravariant => self.subtype_of(t, &self.get_tp_t(rp).unwrap_or(Obj)),
|
||||
Variance::Covariant => self.supertype_of(t, &self.get_tp_t(rp).unwrap_or(Obj)),
|
||||
Variance::Invariant => {
|
||||
let rhs = self.get_tp_t(rp).unwrap_or(Obj);
|
||||
self.same_type_of(t, &rhs) || self.same_type_of(t, &rhs.derefine())
|
||||
}
|
||||
},
|
||||
(_, TyParam::Erased(t)) => match variance {
|
||||
Variance::Contravariant => self.subtype_of(&self.get_tp_t(lp).unwrap_or(Obj), t),
|
||||
Variance::Covariant => self.supertype_of(&self.get_tp_t(lp).unwrap_or(Obj), t),
|
||||
Variance::Invariant => {
|
||||
let lhs = self.get_tp_t(lp).unwrap_or(Obj);
|
||||
self.same_type_of(&lhs, t) || self.same_type_of(&lhs.derefine(), t)
|
||||
}
|
||||
},
|
||||
(TyParam::Array(lp), TyParam::Array(rp)) | (TyParam::Tuple(lp), TyParam::Tuple(rp)) => {
|
||||
for (l, r) in lp.iter().zip(rp.iter()) {
|
||||
if !self.supertype_of_tp(l, r, variance) {
|
||||
|
@ -1298,11 +1292,17 @@ impl Context {
|
|||
| (Pred::LessEqual { .. }, Pred::GreaterEqual { .. })
|
||||
| (Pred::GreaterEqual { .. }, Pred::LessEqual { .. })
|
||||
| (Pred::NotEqual { .. }, Pred::Equal { .. }) => false,
|
||||
(Pred::Equal { rhs, .. }, Pred::Equal { rhs: rhs2, .. })
|
||||
| (Pred::NotEqual { rhs, .. }, Pred::NotEqual { rhs: rhs2, .. }) => self
|
||||
(Pred::NotEqual { rhs, .. }, Pred::NotEqual { rhs: rhs2, .. }) => self
|
||||
.try_cmp(rhs, rhs2)
|
||||
.map(|ord| ord.canbe_eq())
|
||||
.unwrap_or(false),
|
||||
(Pred::Equal { rhs, .. }, Pred::Equal { rhs: rhs2, .. }) => {
|
||||
self.supertype_of_tp(rhs, rhs2, Variance::Covariant)
|
||||
|| self
|
||||
.try_cmp(rhs, rhs2)
|
||||
.map(|ord| ord.canbe_eq())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
// {T >= 0} :> {T >= 1}, {T >= 0} :> {T == 1}
|
||||
(
|
||||
Pred::GreaterEqual { rhs, .. },
|
||||
|
|
|
@ -1411,7 +1411,8 @@ impl Context {
|
|||
let qtps = qt.typarams();
|
||||
let stps = st.typarams();
|
||||
if qtps.len() != stps.len() {
|
||||
log!(err "{} {}", erg_common::fmt_vec(&qtps), erg_common::fmt_vec(&stps));
|
||||
log!(err "{qt} / {st}");
|
||||
log!(err "[{}] [{}]", erg_common::fmt_vec(&qtps), erg_common::fmt_vec(&stps));
|
||||
return Ok(()); // TODO: e.g. Sub(Int) / Eq and Sub(?T)
|
||||
}
|
||||
for (qtp, stp) in qtps.into_iter().zip(stps.into_iter()) {
|
||||
|
|
|
@ -67,13 +67,6 @@ impl Context {
|
|||
Visibility::BUILTIN_PUBLIC,
|
||||
);
|
||||
obj.register_trait(Obj, obj_in);
|
||||
let mut obj_mutizable = Self::builtin_methods(Some(mono(MUTIZABLE)), 1);
|
||||
obj_mutizable.register_builtin_const(
|
||||
MUTABLE_MUT_TYPE,
|
||||
Visibility::BUILTIN_PUBLIC,
|
||||
ValueObj::builtin_trait(mono(MUTABLE_OBJ)),
|
||||
);
|
||||
obj.register_trait(Obj, obj_mutizable);
|
||||
// Obj does not implement Eq
|
||||
let mut complex = Self::builtin_mono_class(COMPLEX, 2);
|
||||
complex.register_superclass(Obj, &obj);
|
||||
|
|
|
@ -32,7 +32,7 @@ use crate::module::SharedCompilerResource;
|
|||
use crate::ty::constructors::*;
|
||||
use crate::ty::free::Constraint;
|
||||
use crate::ty::value::ValueObj;
|
||||
use crate::ty::{BuiltinConstSubr, ConstSubr, Predicate, Type, Visibility};
|
||||
use crate::ty::{BuiltinConstSubr, ConstSubr, ParamTy, Predicate, Type, Visibility};
|
||||
use crate::varinfo::{AbsLocation, Mutability, VarInfo, VarKind};
|
||||
use Mutability::*;
|
||||
use ParamSpec as PS;
|
||||
|
@ -790,7 +790,13 @@ impl Context {
|
|||
_ => ValueObj::builtin_type(t.clone()),
|
||||
};
|
||||
let name = VarName::from_str(t.local_name());
|
||||
let meta_t = v_enum(set! { val.clone() });
|
||||
// e.g Array!: |T, N|(_: {T}, _: {N}) -> {Array!(T, N)}
|
||||
let params = t
|
||||
.typarams()
|
||||
.into_iter()
|
||||
.map(|tp| ParamTy::Pos(tp_enum(self.get_tp_t(&tp).unwrap_or(Obj), set! { tp })))
|
||||
.collect();
|
||||
let meta_t = func(params, None, vec![], v_enum(set! { val.clone() })).quantify();
|
||||
if !cfg!(feature = "py_compat") {
|
||||
self.locals.insert(
|
||||
name.clone(),
|
||||
|
|
|
@ -14,6 +14,7 @@ use crate::feature_error;
|
|||
use crate::ty::constructors::*;
|
||||
use crate::ty::free::{Constraint, HasLevel};
|
||||
use crate::ty::typaram::{TyParam, TyParamLambda};
|
||||
use crate::ty::ValueObj;
|
||||
use crate::ty::{HasType, Predicate, Type};
|
||||
use crate::{type_feature_error, unreachable_error};
|
||||
use Type::*;
|
||||
|
@ -364,6 +365,10 @@ impl Context {
|
|||
let t = self.instantiate_t_inner(*t, tmp_tv_cache, loc)?;
|
||||
Ok(TyParam::t(t))
|
||||
}
|
||||
TyParam::Value(ValueObj::Type(t)) => {
|
||||
let t = self.instantiate_t_inner(t.into_typ(), tmp_tv_cache, loc)?;
|
||||
Ok(TyParam::t(t))
|
||||
}
|
||||
p @ (TyParam::Value(_)
|
||||
| TyParam::Mono(_)
|
||||
| TyParam::FreeVar(_)
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
//!
|
||||
//! `Context` is used for type inference and type checking.
|
||||
#![allow(clippy::result_unit_err)]
|
||||
pub mod cache;
|
||||
pub mod compare;
|
||||
pub mod eval;
|
||||
pub mod generalize;
|
||||
|
|
|
@ -282,15 +282,11 @@ impl Context {
|
|||
return Ok(vi);
|
||||
}
|
||||
let muty = Mutability::from(&ident.inspect()[..]);
|
||||
let py_name = if let Some(vi) = self
|
||||
let opt_vi = self
|
||||
.decls
|
||||
.remove(ident.inspect())
|
||||
.or_else(|| self.future_defined_locals.remove(ident.inspect()))
|
||||
{
|
||||
vi.py_name
|
||||
} else {
|
||||
py_name
|
||||
};
|
||||
.or_else(|| self.future_defined_locals.remove(ident.inspect()));
|
||||
let py_name = opt_vi.as_ref().map_or(py_name, |vi| vi.py_name.clone());
|
||||
let kind = if id.0 == 0 {
|
||||
VarKind::Declared
|
||||
} else {
|
||||
|
|
|
@ -1228,7 +1228,7 @@ impl Context {
|
|||
/// unify(Nat, Int!) == Some(Int)
|
||||
/// unify(Eq, Int) == None
|
||||
/// ```
|
||||
pub fn unify(&self, lhs: &Type, rhs: &Type) -> Option<Type> {
|
||||
pub(crate) fn unify(&self, lhs: &Type, rhs: &Type) -> Option<Type> {
|
||||
let l_sups = self._get_super_classes(lhs)?;
|
||||
let r_sups = self._get_super_classes(rhs)?;
|
||||
for l_sup in l_sups {
|
||||
|
|
|
@ -2147,6 +2147,8 @@ impl Type {
|
|||
/// assert!((A or B).contains_union(B))
|
||||
pub fn contains_union(&self, typ: &Type) -> bool {
|
||||
match self {
|
||||
Type::FreeVar(fv) if fv.is_linked() => fv.crack().contains_union(typ),
|
||||
Type::Refinement(refine) => refine.t.contains_union(typ),
|
||||
Type::Or(t1, t2) => t1.contains_union(typ) || t2.contains_union(typ),
|
||||
_ => self == typ,
|
||||
}
|
||||
|
@ -2290,6 +2292,9 @@ impl Type {
|
|||
/// assert i.Real == 1
|
||||
/// i: (Int)
|
||||
/// ```
|
||||
/// ```
|
||||
/// ?T(:> ?U(:> Int)).coerce(): ?T == ?U == Int
|
||||
/// ```
|
||||
pub fn coerce(&self) {
|
||||
match self {
|
||||
Type::FreeVar(fv) if fv.is_linked() => {
|
||||
|
@ -2297,6 +2302,7 @@ impl Type {
|
|||
}
|
||||
Type::FreeVar(fv) if fv.is_unbound() => {
|
||||
let (sub, _sup) = fv.get_subsup().unwrap();
|
||||
sub.coerce();
|
||||
fv.link(&sub);
|
||||
}
|
||||
Type::And(l, r) | Type::Or(l, r) => {
|
||||
|
|
|
@ -2521,6 +2521,10 @@ impl TypeSpec {
|
|||
None,
|
||||
))
|
||||
}
|
||||
|
||||
pub fn poly(ident: Identifier, args: ConstArgs) -> Self {
|
||||
Self::PreDeclTy(PreDeclTypeSpec::Simple(SimpleTypeSpec::new(ident, args)))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
|
|
|
@ -10,3 +10,6 @@ i_s = ![1 as (Int or Str)]
|
|||
i_s.push! "b"
|
||||
i_s.push! 2
|
||||
i_s.push! None # ERR
|
||||
|
||||
_: Array!(Int, _) = !["a"] # ERR
|
||||
_: Array!(Int, 1) = ![1, 2] # ERR
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
id x = x
|
||||
|
||||
ids = Array(map(id, ["1", "2", "3"]))
|
||||
ints = Array(map(int, ["1", "2", "3"]))
|
||||
ids = Array.__call__(map(id, ["1", "2", "3"]))
|
||||
ints = Array.__call__(map(int, ["1", "2", "3"]))
|
||||
|
||||
assert ids == ["1", "2", "3"]
|
||||
assert ints == [1, 2, 3]
|
||||
|
|
|
@ -5,3 +5,7 @@ for! 0..<10, i =>
|
|||
assert v[0] == 0
|
||||
assert v == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
|
||||
log sum v
|
||||
|
||||
iv: Array!(Int, _) = ![1]
|
||||
iv.push! 2
|
||||
iv: Array!(Int, 2)
|
||||
|
|
|
@ -324,7 +324,7 @@ fn exec_subtyping_err() -> Result<(), ()> {
|
|||
|
||||
#[test]
|
||||
fn exec_callable() -> Result<(), ()> {
|
||||
expect_failure("tests/should_err/callable.er", 0, 5)
|
||||
expect_failure("tests/should_err/callable.er", 0, 6)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -344,7 +344,7 @@ fn exec_mut_err() -> Result<(), ()> {
|
|||
|
||||
#[test]
|
||||
fn exec_mut_array_err() -> Result<(), ()> {
|
||||
expect_failure("tests/should_err/mut_array.er", 0, 2)
|
||||
expect_failure("tests/should_err/mut_array.er", 0, 4)
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue