fix: bugs & remove TypeCmpCache

This commit is contained in:
Shunsuke Shibayama 2023-04-27 13:10:33 +09:00
parent 91fd285ccd
commit 66cac2ccc2
15 changed files with 68 additions and 118 deletions

View file

@ -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));
}
}

View file

@ -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, .. },

View file

@ -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()) {

View file

@ -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);

View file

@ -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(),

View file

@ -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(_)

View file

@ -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;

View file

@ -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 {

View file

@ -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 {

View file

@ -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) => {

View file

@ -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)]

View file

@ -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

View file

@ -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]

View file

@ -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)

View file

@ -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]