Skip to content

Commit

Permalink
fix: forward reference bug for methods
Browse files Browse the repository at this point in the history
  • Loading branch information
mtshiba committed Oct 29, 2023
1 parent 6058339 commit 34a20e7
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 92 deletions.
4 changes: 4 additions & 0 deletions crates/erg_compiler/context/instantiate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -887,6 +887,10 @@ impl Context {
}
Ok(t)
}
Subr(subr) if subr.has_qvar() => {
log!(err "{subr} has qvar");
self.instantiate(Type::Subr(subr).quantify(), callee)
}
// rank-1制限により、通常の型(rank-0型)の内側に量化型は存在しない
other => Ok(other),
}
Expand Down
9 changes: 9 additions & 0 deletions crates/erg_compiler/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1227,6 +1227,15 @@ impl Context {
self.kind = kind;
}

pub(crate) fn replace(&mut self, new: Self) {
let old = mem::take(self);
*self = new;
self.outer = Some(Box::new(old));
self.cfg = self.get_outer().unwrap().cfg.clone();
self.shared = self.get_outer().unwrap().shared.clone();
self.higher_order_caller = self.get_outer().unwrap().higher_order_caller.clone();
}

pub(crate) fn clear_invalid_vars(&mut self) {
self.locals.retain(|_, v| v.t != Failure);
self.decls.retain(|_, v| v.t != Failure);
Expand Down
28 changes: 27 additions & 1 deletion crates/erg_compiler/context/register.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use ast::{
ConstIdentifier, Decorator, DefId, Identifier, OperationKind, PolyTypeSpec, PreDeclTypeSpec,
VarName,
};
use erg_parser::ast::{self, TypeSpecWithOp};
use erg_parser::ast::{self, ClassAttr, TypeSpecWithOp};

use crate::ty::constructors::{
free_var, func, func0, func1, proc, ref_, ref_mut, tp_enum, unknown_len_array_t, v_enum,
Expand Down Expand Up @@ -1100,6 +1100,9 @@ impl Context {
if let Err(errs) = self.register_const_def(&class_def.def) {
total_errs.extend(errs);
}
let vis = self
.instantiate_vis_modifier(class_def.def.sig.vis())
.unwrap_or(VisibilityModifier::Public);
for methods in class_def.methods_list.iter() {
let Ok((class, impl_trait)) = self.get_class_and_impl_trait(&methods.class)
else {
Expand All @@ -1112,6 +1115,29 @@ impl Context {
total_errs.extend(errs);
}
}
let kind =
ContextKind::MethodDefs(impl_trait.as_ref().map(|(t, _)| t.clone()));
self.grow(&class.local_name(), kind, vis.clone(), None);
for attr in methods.attrs.iter() {
if let ClassAttr::Def(def) = attr {
if let Err(errs) = self.register_const_def(def) {
total_errs.extend(errs);
}
}
}
let ctx = self.pop();
let Some(class_root) = self.get_mut_nominal_type_ctx(&class) else {
log!(err "class not found: {class}");
continue;
};
let typ = if let Some((impl_trait, _)) = impl_trait {
ClassDefType::impl_trait(class, impl_trait)
} else {
ClassDefType::Simple(class)
};
class_root
.methods_list
.push(MethodContext::new(methods.id, typ, ctx));
}
}
ast::Expr::PatchDef(patch_def) => {
Expand Down
194 changes: 103 additions & 91 deletions crates/erg_compiler/lower.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1912,87 +1912,11 @@ impl ASTLowerer {
decorators.insert(deco);
}
match registered_t {
Type::Subr(subr_t) => {
let params = self.lower_params(sig.params.clone(), Some(&subr_t))?;
if let Err(errs) = self.module.context.register_const(&body.block) {
self.errs.extend(errs);
}
let return_t = subr_t
.return_t
.has_no_unbound_var()
.then_some(subr_t.return_t.as_ref());
match self.lower_block(body.block, return_t) {
Ok(block) => {
let found_body_t = self.module.context.squash_tyvar(block.t());
let vi = match self.module.context.outer.as_mut().unwrap().assign_subr(
&sig,
body.id,
&found_body_t,
block.last().unwrap(),
) {
Ok(vi) => vi,
Err((errs, vi)) => {
self.errs.extend(errs);
vi
}
};
let ident = hir::Identifier::new(sig.ident, None, vi);
let ret_t_spec = if let Some(ts) = sig.return_t_spec {
let spec_t = self.module.context.instantiate_typespec(&ts.t_spec)?;
let expr = self.fake_lower_expr(*ts.t_spec_as_expr.clone())?;
Some(hir::TypeSpecWithOp::new(ts, expr, spec_t))
} else {
None
};
let captured_names = mem::take(&mut self.module.context.captured_names);
let sig = hir::SubrSignature::new(
decorators,
ident,
sig.bounds,
params,
ret_t_spec,
captured_names,
);
let body = hir::DefBody::new(body.op, block, body.id);
Ok(hir::Def::new(hir::Signature::Subr(sig), body))
}
Err(errs) => {
self.errs.extend(errs);
let vi = match self.module.context.outer.as_mut().unwrap().assign_subr(
&sig,
ast::DefId(0),
&Type::Failure,
&sig,
) {
Ok(vi) => vi,
Err((errs, vi)) => {
self.errs.extend(errs);
vi
}
};
let ident = hir::Identifier::new(sig.ident, None, vi);
let ret_t_spec = if let Some(ts) = sig.return_t_spec {
let spec_t = self.module.context.instantiate_typespec(&ts.t_spec)?;
let expr = self.fake_lower_expr(*ts.t_spec_as_expr.clone())?;
Some(hir::TypeSpecWithOp::new(ts, expr, spec_t))
} else {
None
};
let captured_names = mem::take(&mut self.module.context.captured_names);
let sig = hir::SubrSignature::new(
decorators,
ident,
sig.bounds,
params,
ret_t_spec,
captured_names,
);
let block =
hir::Block::new(vec![hir::Expr::Dummy(hir::Dummy::new(vec![]))]);
let body = hir::DefBody::new(body.op, block, body.id);
Ok(hir::Def::new(hir::Signature::Subr(sig), body))
}
}
Type::Subr(subr_t) => self.lower_subr_block(subr_t, sig, decorators, body),
quant @ Type::Quantified(_) => {
let instance = self.module.context.instantiate_dummy(quant)?;
let subr_t = SubrType::try_from(instance).unwrap();
self.lower_subr_block(subr_t, sig, decorators, body)
}
Type::Failure => {
let params = self.lower_params(sig.params, None)?;
Expand Down Expand Up @@ -2030,6 +1954,94 @@ impl ASTLowerer {
}
}

fn lower_subr_block(
&mut self,
subr_t: SubrType,
sig: ast::SubrSignature,
decorators: Set<hir::Expr>,
body: ast::DefBody,
) -> LowerResult<hir::Def> {
let params = self.lower_params(sig.params.clone(), Some(&subr_t))?;
if let Err(errs) = self.module.context.register_const(&body.block) {
self.errs.extend(errs);
}
let return_t = subr_t
.return_t
.has_no_unbound_var()
.then_some(subr_t.return_t.as_ref());
match self.lower_block(body.block, return_t) {
Ok(block) => {
let found_body_t = self.module.context.squash_tyvar(block.t());
let vi = match self.module.context.outer.as_mut().unwrap().assign_subr(
&sig,
body.id,
&found_body_t,
block.last().unwrap(),
) {
Ok(vi) => vi,
Err((errs, vi)) => {
self.errs.extend(errs);
vi
}
};
let ident = hir::Identifier::new(sig.ident, None, vi);
let ret_t_spec = if let Some(ts) = sig.return_t_spec {
let spec_t = self.module.context.instantiate_typespec(&ts.t_spec)?;
let expr = self.fake_lower_expr(*ts.t_spec_as_expr.clone())?;
Some(hir::TypeSpecWithOp::new(ts, expr, spec_t))
} else {
None
};
let captured_names = mem::take(&mut self.module.context.captured_names);
let sig = hir::SubrSignature::new(
decorators,
ident,
sig.bounds,
params,
ret_t_spec,
captured_names,
);
let body = hir::DefBody::new(body.op, block, body.id);
Ok(hir::Def::new(hir::Signature::Subr(sig), body))
}
Err(errs) => {
self.errs.extend(errs);
let vi = match self.module.context.outer.as_mut().unwrap().assign_subr(
&sig,
ast::DefId(0),
&Type::Failure,
&sig,
) {
Ok(vi) => vi,
Err((errs, vi)) => {
self.errs.extend(errs);
vi
}
};
let ident = hir::Identifier::new(sig.ident, None, vi);
let ret_t_spec = if let Some(ts) = sig.return_t_spec {
let spec_t = self.module.context.instantiate_typespec(&ts.t_spec)?;
let expr = self.fake_lower_expr(*ts.t_spec_as_expr.clone())?;
Some(hir::TypeSpecWithOp::new(ts, expr, spec_t))
} else {
None
};
let captured_names = mem::take(&mut self.module.context.captured_names);
let sig = hir::SubrSignature::new(
decorators,
ident,
sig.bounds,
params,
ret_t_spec,
captured_names,
);
let block = hir::Block::new(vec![hir::Expr::Dummy(hir::Dummy::new(vec![]))]);
let body = hir::DefBody::new(body.op, block, body.id);
Ok(hir::Def::new(hir::Signature::Subr(sig), body))
}
}
}

fn lower_class_def(&mut self, class_def: ast::ClassDef) -> LowerResult<hir::ClassDef> {
log!(info "entered {}({class_def})", fn_name!());
let mut hir_def = self.lower_def(class_def.def)?;
Expand Down Expand Up @@ -2060,20 +2072,20 @@ impl ASTLowerer {
self.module.context.get_similar_name(&class.local_name()),
)));
}
let kind = ContextKind::MethodDefs(impl_trait.as_ref().map(|(t, _)| t.clone()));
self.module
let methods_list = &mut self
.module
.context
.grow(&class.local_name(), kind, hir_def.sig.vis().clone(), None);
.get_mut_nominal_type_ctx(&class)
.unwrap()
.methods_list;
let methods_idx = methods_list.iter().position(|m| m.id == methods.id);
let methods_ctx = methods_idx
.map(|idx| methods_list.remove(idx))
.unwrap_or_else(|| todo!());
self.module.context.replace(methods_ctx.ctx);
for attr in methods.attrs.iter() {
match attr {
ast::ClassAttr::Def(def) => {
self.module
.context
.register_const_def(def)
.map_err(|errs| {
self.pop_append_errs();
errs
})?;
if let Some(ident) = def.sig.ident() {
if self
.module
Expand Down
6 changes: 6 additions & 0 deletions tests/should_ok/class.er
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@ _ = Test!

# forward reference
C = Class { .x = D }
C|<: Eq|.
`==` self, other = self.x == other.x
C.
foo self, x = self.x.foo(x)
D = Class { .y = Int }
D.
new y = Self::__new__ { .y; }
@staticmethod
foo x = x + 1
one = Self.new 1
D|<: Eq|.
`==` self, other = self.y == other.y

d = D.new 1
assert d.foo(1) == 2
Expand Down

0 comments on commit 34a20e7

Please sign in to comment.