fix: heterogenous array specifying

This commit is contained in:
Shunsuke Shibayama 2023-02-20 00:08:35 +09:00
parent 7103c9f3bb
commit 51d84fff9e
10 changed files with 132 additions and 89 deletions

View file

@ -29,7 +29,7 @@ homepage = "https://erg-lang.org/"
[features]
# when "debug" feature is turned on, that of the following crates will also be turned on.
debug = ["erg_common/debug", "erg_parser/debug", "erg_compiler/debug", "els/debug"]
debug = ["erg_common/debug", "erg_parser/debug", "erg_compiler/debug"] # "els/debug"
japanese = [
"erg_common/japanese",
"erg_parser/japanese",

View file

@ -164,7 +164,7 @@ impl<Checker: BuildRunnable> Server<Checker> {
let mut result = vec![];
result.extend(self.get_block_hint(&def.body.block));
let Signature::Subr(subr) = &def.sig else { unreachable!() };
if subr.ref_t().is_quantified() && subr.bounds.is_empty() {
if subr.ref_t().is_quantified_subr() && subr.bounds.is_empty() {
let subr = subr.ref_t().to_string();
let ty_bounds = format!("|{}|", subr.split('|').nth(1).unwrap_or(""));
let hint = type_bounds_anot(

View file

@ -1,10 +1,7 @@
use std::mem;
use erg_common::set::Set;
use erg_common::traits::{Locational, Stream};
use erg_common::Str;
use erg_common::{assume_unreachable, fn_name};
use erg_common::{dict, set};
use erg_common::{assume_unreachable, dict, fn_name, set};
#[allow(unused_imports)]
use erg_common::{fmt_vec, log};
@ -567,19 +564,31 @@ impl Context {
}
Type::Subr(mut subr) => {
for param in subr.non_default_params.iter_mut() {
*param.typ_mut() =
self.deref_tyvar(mem::take(param.typ_mut()), Contravariant, loc)?;
*param.typ_mut() = self.deref_tyvar(
mem::take(param.typ_mut()),
variance * Contravariant,
loc,
)?;
}
if let Some(var_args) = &mut subr.var_params {
*var_args.typ_mut() =
self.deref_tyvar(mem::take(var_args.typ_mut()), Contravariant, loc)?;
*var_args.typ_mut() = self.deref_tyvar(
mem::take(var_args.typ_mut()),
variance * Contravariant,
loc,
)?;
}
for d_param in subr.default_params.iter_mut() {
*d_param.typ_mut() =
self.deref_tyvar(mem::take(d_param.typ_mut()), Contravariant, loc)?;
*d_param.typ_mut() = self.deref_tyvar(
mem::take(d_param.typ_mut()),
variance * Contravariant,
loc,
)?;
}
subr.return_t =
Box::new(self.deref_tyvar(mem::take(&mut subr.return_t), Covariant, loc)?);
subr.return_t = Box::new(self.deref_tyvar(
mem::take(&mut subr.return_t),
variance * Covariant,
loc,
)?);
Ok(Type::Subr(subr))
}
Type::Quantified(subr)
@ -730,7 +739,7 @@ impl Context {
self.level = 0;
let mut errs = TyCheckErrors::empty();
for chunk in hir.module.iter_mut() {
if let Err(es) = self.resolve_expr_t(chunk, &mut set! {}) {
if let Err(es) = self.resolve_expr_t(chunk) {
errs.extend(es);
}
}
@ -747,12 +756,12 @@ impl Context {
let mut params = mem::take(&mut self.params);
let mut methods_list = mem::take(&mut self.methods_list);
for (name, vi) in locals.iter_mut() {
if let Ok(t) = self.deref_tyvar(mem::take(&mut vi.t), Variance::Covariant, name) {
if let Ok(t) = self.deref_tyvar(mem::take(&mut vi.t), Covariant, name) {
vi.t = t;
}
}
for (name, vi) in params.iter_mut() {
if let Ok(t) = self.deref_tyvar(mem::take(&mut vi.t), Variance::Covariant, name) {
if let Ok(t) = self.deref_tyvar(mem::take(&mut vi.t), Covariant, name) {
vi.t = t;
}
}
@ -764,18 +773,14 @@ impl Context {
self.methods_list = methods_list;
}
fn resolve_params_t(
&self,
params: &mut hir::Params,
qvars: &mut Set<Str>,
) -> TyCheckResult<()> {
fn resolve_params_t(&self, params: &mut hir::Params) -> TyCheckResult<()> {
for param in params.non_defaults.iter_mut() {
if !qvars.contains(&param.vi.t.qual_name()) {
if !param.vi.t.is_qvar() {
param.vi.t = self.deref_tyvar(mem::take(&mut param.vi.t), Contravariant, param)?;
}
}
if let Some(var_params) = &mut params.var_params {
if !qvars.contains(&var_params.vi.t.qual_name()) {
if !var_params.vi.t.is_qvar() {
var_params.vi.t = self.deref_tyvar(
mem::take(&mut var_params.vi.t),
Contravariant,
@ -784,20 +789,20 @@ impl Context {
}
}
for param in params.defaults.iter_mut() {
if !qvars.contains(&param.sig.vi.t.qual_name()) {
if !param.sig.vi.t.is_qvar() {
param.sig.vi.t =
self.deref_tyvar(mem::take(&mut param.sig.vi.t), Contravariant, param)?;
}
self.resolve_expr_t(&mut param.default_val, qvars)?;
self.resolve_expr_t(&mut param.default_val)?;
}
Ok(())
}
fn resolve_expr_t(&self, expr: &mut hir::Expr, qvars: &mut Set<Str>) -> TyCheckResult<()> {
fn resolve_expr_t(&self, expr: &mut hir::Expr) -> TyCheckResult<()> {
match expr {
hir::Expr::Lit(_) => Ok(()),
hir::Expr::Accessor(acc) => {
if !qvars.contains(&acc.ref_t().qual_name()) {
if !acc.ref_t().is_qvar() {
let variance = if acc.var_info().kind.is_parameter() {
Contravariant
} else {
@ -807,7 +812,7 @@ impl Context {
*acc.ref_mut_t() = self.deref_tyvar(t, variance, acc)?;
}
if let hir::Accessor::Attr(attr) = acc {
self.resolve_expr_t(&mut attr.obj, qvars)?;
self.resolve_expr_t(&mut attr.obj)?;
}
Ok(())
}
@ -815,14 +820,14 @@ impl Context {
hir::Array::Normal(arr) => {
arr.t = self.deref_tyvar(mem::take(&mut arr.t), Covariant, arr)?;
for elem in arr.elems.pos_args.iter_mut() {
self.resolve_expr_t(&mut elem.expr, qvars)?;
self.resolve_expr_t(&mut elem.expr)?;
}
Ok(())
}
hir::Array::WithLength(arr) => {
arr.t = self.deref_tyvar(mem::take(&mut arr.t), Covariant, arr)?;
self.resolve_expr_t(&mut arr.elem, qvars)?;
self.resolve_expr_t(&mut arr.len, qvars)?;
self.resolve_expr_t(&mut arr.elem)?;
self.resolve_expr_t(&mut arr.len)?;
Ok(())
}
other => feature_error!(
@ -837,7 +842,7 @@ impl Context {
hir::Tuple::Normal(tup) => {
tup.t = self.deref_tyvar(mem::take(&mut tup.t), Covariant, tup)?;
for elem in tup.elems.pos_args.iter_mut() {
self.resolve_expr_t(&mut elem.expr, qvars)?;
self.resolve_expr_t(&mut elem.expr)?;
}
Ok(())
}
@ -846,14 +851,14 @@ impl Context {
hir::Set::Normal(st) => {
st.t = self.deref_tyvar(mem::take(&mut st.t), Covariant, st)?;
for elem in st.elems.pos_args.iter_mut() {
self.resolve_expr_t(&mut elem.expr, qvars)?;
self.resolve_expr_t(&mut elem.expr)?;
}
Ok(())
}
hir::Set::WithLength(st) => {
st.t = self.deref_tyvar(mem::take(&mut st.t), Covariant, st)?;
self.resolve_expr_t(&mut st.elem, qvars)?;
self.resolve_expr_t(&mut st.len, qvars)?;
self.resolve_expr_t(&mut st.elem)?;
self.resolve_expr_t(&mut st.len)?;
Ok(())
}
},
@ -861,8 +866,8 @@ impl Context {
hir::Dict::Normal(dic) => {
dic.t = self.deref_tyvar(mem::take(&mut dic.t), Covariant, dic)?;
for kv in dic.kvs.iter_mut() {
self.resolve_expr_t(&mut kv.key, qvars)?;
self.resolve_expr_t(&mut kv.value, qvars)?;
self.resolve_expr_t(&mut kv.key)?;
self.resolve_expr_t(&mut kv.value)?;
}
Ok(())
}
@ -888,7 +893,7 @@ impl Context {
}
}
for chunk in attr.body.block.iter_mut() {
self.resolve_expr_t(chunk, qvars)?;
self.resolve_expr_t(chunk)?;
}
}
Ok(())
@ -896,14 +901,14 @@ impl Context {
hir::Expr::BinOp(binop) => {
let t = mem::take(binop.signature_mut_t().unwrap());
*binop.signature_mut_t().unwrap() = self.deref_tyvar(t, Covariant, binop)?;
self.resolve_expr_t(&mut binop.lhs, qvars)?;
self.resolve_expr_t(&mut binop.rhs, qvars)?;
self.resolve_expr_t(&mut binop.lhs)?;
self.resolve_expr_t(&mut binop.rhs)?;
Ok(())
}
hir::Expr::UnaryOp(unaryop) => {
let t = mem::take(unaryop.signature_mut_t().unwrap());
*unaryop.signature_mut_t().unwrap() = self.deref_tyvar(t, Covariant, unaryop)?;
self.resolve_expr_t(&mut unaryop.expr, qvars)?;
self.resolve_expr_t(&mut unaryop.expr)?;
Ok(())
}
hir::Expr::Call(call) => {
@ -911,78 +916,68 @@ impl Context {
let t = mem::take(t);
*call.signature_mut_t().unwrap() = self.deref_tyvar(t, Covariant, call)?;
}
self.resolve_expr_t(&mut call.obj, qvars)?;
self.resolve_expr_t(&mut call.obj)?;
for arg in call.args.pos_args.iter_mut() {
self.resolve_expr_t(&mut arg.expr, qvars)?;
self.resolve_expr_t(&mut arg.expr)?;
}
if let Some(var_args) = &mut call.args.var_args {
self.resolve_expr_t(&mut var_args.expr, qvars)?;
self.resolve_expr_t(&mut var_args.expr)?;
}
for arg in call.args.kw_args.iter_mut() {
self.resolve_expr_t(&mut arg.expr, qvars)?;
self.resolve_expr_t(&mut arg.expr)?;
}
Ok(())
}
hir::Expr::Def(def) => {
*def.sig.ref_mut_t() =
self.deref_tyvar(mem::take(def.sig.ref_mut_t()), Covariant, &def.sig)?;
let subr_qvars_ = def.sig.ref_t().qvars();
let subr_qvars = subr_qvars_.clone().into_iter().map(|(name, _)| name);
qvars.extend(subr_qvars);
if let Some(params) = def.sig.params_mut() {
self.resolve_params_t(params, qvars)?;
self.resolve_params_t(params)?;
}
for chunk in def.body.block.iter_mut() {
self.resolve_expr_t(chunk, qvars)?;
}
for (name, _) in subr_qvars_.into_iter() {
qvars.remove(&name);
self.resolve_expr_t(chunk)?;
}
Ok(())
}
hir::Expr::Lambda(lambda) => {
log!(err "{}", lambda.t);
lambda.t = self.deref_tyvar(mem::take(&mut lambda.t), Covariant, lambda)?;
let subr_qvars_ = lambda.ref_t().qvars();
let subr_qvars = subr_qvars_.clone().into_iter().map(|(name, _)| name);
qvars.extend(subr_qvars);
self.resolve_params_t(&mut lambda.params, qvars)?;
log!(err "{}", lambda.t);
self.resolve_params_t(&mut lambda.params)?;
for chunk in lambda.body.iter_mut() {
self.resolve_expr_t(chunk, qvars)?;
}
for (name, _) in subr_qvars_.into_iter() {
qvars.remove(&name);
self.resolve_expr_t(chunk)?;
}
Ok(())
}
hir::Expr::ClassDef(class_def) => {
for def in class_def.methods.iter_mut() {
self.resolve_expr_t(def, qvars)?;
self.resolve_expr_t(def)?;
}
Ok(())
}
hir::Expr::PatchDef(patch_def) => {
for def in patch_def.methods.iter_mut() {
self.resolve_expr_t(def, qvars)?;
self.resolve_expr_t(def)?;
}
Ok(())
}
hir::Expr::ReDef(redef) => {
// REVIEW: redef.attr is not dereferenced
for chunk in redef.block.iter_mut() {
self.resolve_expr_t(chunk, qvars)?;
self.resolve_expr_t(chunk)?;
}
Ok(())
}
hir::Expr::TypeAsc(tasc) => self.resolve_expr_t(&mut tasc.expr, qvars),
hir::Expr::TypeAsc(tasc) => self.resolve_expr_t(&mut tasc.expr),
hir::Expr::Code(chunks) | hir::Expr::Compound(chunks) => {
for chunk in chunks.iter_mut() {
self.resolve_expr_t(chunk, qvars)?;
self.resolve_expr_t(chunk)?;
}
Ok(())
}
hir::Expr::Dummy(chunks) => {
for chunk in chunks.iter_mut() {
self.resolve_expr_t(chunk, qvars)?;
self.resolve_expr_t(chunk)?;
}
Ok(())
}

View file

@ -1066,7 +1066,7 @@ impl Context {
}
if sub != Never {
instance.coerce();
if instance.is_quantified() {
if instance.is_quantified_subr() {
let instance = self.instantiate(instance.clone(), obj)?;
self.substitute_call(obj, attr_name, &instance, pos_args, kw_args)?;
return Ok(SubstituteResult::Coerced(instance));
@ -1585,7 +1585,7 @@ impl Context {
SubstituteResult::__Call__(__call__) => __call__,
SubstituteResult::Coerced(coerced) => coerced,
};
debug_assert!(!instance.is_quantified());
debug_assert!(!instance.is_quantified_subr());
log!(info "Substituted:\ninstance: {instance}");
let res = self.eval_t_params(instance, self.level, obj)?;
log!(info "Params evaluated:\nres: {res}\n");

View file

@ -1473,7 +1473,7 @@ impl Context {
Ok(ty)
}
// HACK: {op: |T|(T -> T) | op == F} => ?T -> ?T
Refinement(refine) if refine.t.is_quantified() => {
Refinement(refine) if refine.t.is_quantified_subr() => {
let quant = enum_unwrap!(*refine.t, Type::Quantified);
let mut tmp_tv_cache = TyVarCache::new(self.level, self);
let t = self.instantiate_t_inner(*quant, &mut tmp_tv_cache, callee)?;

View file

@ -112,16 +112,31 @@ impl DefaultInfo {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
pub enum Variance {
/// Output(T)
Covariant, // 共変
/// Input(T)
Contravariant, // 反変
/// Output(T), 共変, +
Covariant,
/// Input(T), 反変, -
Contravariant,
/// 不変, 0
#[default]
Invariant, // 不変
Invariant,
}
impl_display_from_debug!(Variance);
impl std::ops::Mul for Variance {
type Output = Self;
fn mul(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Variance::Covariant, Variance::Covariant)
| (Variance::Contravariant, Variance::Contravariant) => Variance::Covariant,
(Variance::Covariant, Variance::Contravariant)
| (Variance::Contravariant, Variance::Covariant) => Variance::Contravariant,
_ => Variance::Invariant,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct ParamSpec {
pub(crate) name: Option<&'static str>,

View file

@ -455,8 +455,12 @@ impl ASTLowerer {
);
let ident = hir::Identifier::new(ident.dot, ident.name, None, vi);
let t_spec_expr = self.fake_lower_expr(*tasc.t_spec.t_spec_as_expr)?;
let t_spec =
hir::TypeSpecWithOp::new(tasc.t_spec.op, tasc.t_spec.t_spec, t_spec_expr);
let t_spec = hir::TypeSpecWithOp::new(
tasc.t_spec.op,
tasc.t_spec.t_spec,
t_spec_expr,
Type::Failure,
);
Ok(hir::Expr::Accessor(hir::Accessor::Ident(ident)).type_asc(t_spec))
}
ast::Expr::Accessor(ast::Accessor::Attr(mut attr)) => {
@ -499,8 +503,12 @@ impl ASTLowerer {
let ident = hir::Identifier::new(attr.ident.dot, attr.ident.name, None, vi);
let attr = obj.attr_expr(ident);
let t_spec_expr = self.fake_lower_expr(*tasc.t_spec.t_spec_as_expr)?;
let t_spec =
hir::TypeSpecWithOp::new(tasc.t_spec.op, tasc.t_spec.t_spec, t_spec_expr);
let t_spec = hir::TypeSpecWithOp::new(
tasc.t_spec.op,
tasc.t_spec.t_spec,
t_spec_expr,
Type::Failure,
);
Ok(attr.type_asc(t_spec))
}
other => Err(LowerErrors::from(LowerError::declare_error(

View file

@ -2313,6 +2313,7 @@ pub struct TypeSpecWithOp {
pub op: Token,
pub t_spec: TypeSpec,
pub t_spec_as_expr: Box<Expr>,
pub spec_t: Type,
}
impl NestedDisplay for TypeSpecWithOp {
@ -2325,11 +2326,12 @@ impl_display_from_nested!(TypeSpecWithOp);
impl_locational!(TypeSpecWithOp, lossy op, t_spec);
impl TypeSpecWithOp {
pub fn new(op: Token, t_spec: TypeSpec, t_spec_as_expr: Expr) -> Self {
pub fn new(op: Token, t_spec: TypeSpec, t_spec_as_expr: Expr, spec_t: Type) -> Self {
Self {
op,
t_spec,
t_spec_as_expr: Box::new(t_spec_as_expr),
spec_t,
}
}
}

View file

@ -251,11 +251,23 @@ impl ASTLowerer {
let mut union = Type::Never;
for elem in elems {
let elem = self.lower_expr(elem.expr)?;
union = self.module.context.union(&union, elem.ref_t());
if let Some((l, r)) = union.union_types() {
let union_ = self.module.context.union(&union, elem.ref_t());
if let Some((l, r)) = union_.union_types() {
match (l.is_unbound_var(), r.is_unbound_var()) {
// e.g. [1, "a"]
(false, false) => {
return Err(self.elem_err(&l, &r, &elem));
if let hir::Expr::TypeAsc(type_asc) = &elem {
// e.g. [1, "a": Str or NoneType]
if !self
.module
.context
.supertype_of(&type_asc.spec.spec_t, &union)
{
return Err(self.elem_err(&l, &r, &elem));
} // else(OK): e.g. [1, "a": Str or Int]
} else {
return Err(self.elem_err(&l, &r, &elem));
}
}
// TODO: check if the type is compatible with the other type
(true, false) => {}
@ -263,6 +275,7 @@ impl ASTLowerer {
(true, true) => {}
}
}
union = union_;
new_array.push(elem);
}
let elem_t = if union == Type::Never {
@ -915,12 +928,14 @@ impl ASTLowerer {
fn lower_type_spec_with_op(
&mut self,
type_spec_with_op: ast::TypeSpecWithOp,
spec_t: Type,
) -> LowerResult<hir::TypeSpecWithOp> {
let expr = self.fake_lower_expr(*type_spec_with_op.t_spec_as_expr)?;
Ok(hir::TypeSpecWithOp::new(
type_spec_with_op.op,
type_spec_with_op.t_spec,
expr,
spec_t,
))
}
@ -1984,7 +1999,7 @@ impl ASTLowerer {
)));
}
}
let t_spec = self.lower_type_spec_with_op(tasc.t_spec)?;
let t_spec = self.lower_type_spec_with_op(tasc.t_spec, spec_t)?;
Ok(expr.type_asc(t_spec))
}
@ -2063,7 +2078,7 @@ impl ASTLowerer {
.map(|ctx| ctx.name.clone());
let ident = hir::Identifier::new(ident.dot, ident.name, qual_name, ident_vi);
let expr = hir::Expr::Accessor(hir::Accessor::Ident(ident));
let t_spec = self.lower_type_spec_with_op(tasc.t_spec)?;
let t_spec = self.lower_type_spec_with_op(tasc.t_spec, spec_t)?;
Ok(expr.type_asc(t_spec))
}

View file

@ -1291,11 +1291,11 @@ impl Type {
}
}
pub fn is_quantified(&self) -> bool {
pub fn is_quantified_subr(&self) -> bool {
match self {
Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_quantified(),
Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_quantified_subr(),
Self::Quantified(_) => true,
Self::Refinement(refine) => refine.t.is_quantified(),
Self::Refinement(refine) => refine.t.is_quantified_subr(),
_ => false,
}
}
@ -1603,6 +1603,14 @@ impl Type {
self.qvars().iter().any(|(_, c)| c.is_uninited())
}
pub fn is_qvar(&self) -> bool {
match self {
Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_qvar(),
Self::FreeVar(fv) if fv.is_generalized() => true,
_ => false,
}
}
/// if the type is polymorphic
pub fn has_qvar(&self) -> bool {
match self {