mirror of
https://github.com/erg-lang/erg.git
synced 2025-09-30 04:44:44 +00:00
WIP: Impl DataPack type checking
This commit is contained in:
parent
e7898c094e
commit
263c43d74b
7 changed files with 119 additions and 58 deletions
|
@ -101,7 +101,7 @@ impl Context {
|
||||||
let mut tv_ctx = TyVarContext::new(self.level, ctx.type_params_bounds(), self);
|
let mut tv_ctx = TyVarContext::new(self.level, ctx.type_params_bounds(), self);
|
||||||
let t = Self::instantiate_t(t, &mut tv_ctx);
|
let t = Self::instantiate_t(t, &mut tv_ctx);
|
||||||
if let Some((_, root_ctx)) = self.poly_types.get_mut(&t.name()) {
|
if let Some((_, root_ctx)) = self.poly_types.get_mut(&t.name()) {
|
||||||
root_ctx.specializations.push((t, ctx));
|
root_ctx.method_defs.push((t, ctx));
|
||||||
} else {
|
} else {
|
||||||
let name = VarName::from_str(t.name());
|
let name = VarName::from_str(t.name());
|
||||||
self.locals
|
self.locals
|
||||||
|
|
|
@ -192,6 +192,7 @@ pub enum ContextKind {
|
||||||
Func,
|
Func,
|
||||||
Proc,
|
Proc,
|
||||||
Class,
|
Class,
|
||||||
|
MethodDefs,
|
||||||
Trait,
|
Trait,
|
||||||
StructuralTrait,
|
StructuralTrait,
|
||||||
Patch(Type),
|
Patch(Type),
|
||||||
|
@ -230,8 +231,8 @@ pub struct Context {
|
||||||
// patchによってsuper class/traitになったものはここに含まれない
|
// patchによってsuper class/traitになったものはここに含まれない
|
||||||
pub(crate) super_classes: Vec<Type>, // if self is a patch, means patch classes
|
pub(crate) super_classes: Vec<Type>, // if self is a patch, means patch classes
|
||||||
pub(crate) super_traits: Vec<Type>, // if self is not a trait, means implemented traits
|
pub(crate) super_traits: Vec<Type>, // if self is not a trait, means implemented traits
|
||||||
// specialized contexts, If self is a type
|
// method definitions, if the context is a type
|
||||||
pub(crate) specializations: Vec<(Type, Context)>,
|
pub(crate) method_defs: Vec<(Type, Context)>,
|
||||||
/// K: method name, V: impl patch
|
/// K: method name, V: impl patch
|
||||||
/// Provided methods can switch implementations on a scope-by-scope basis
|
/// Provided methods can switch implementations on a scope-by-scope basis
|
||||||
/// K: メソッド名, V: それを実装するパッチたち
|
/// K: メソッド名, V: それを実装するパッチたち
|
||||||
|
@ -357,7 +358,7 @@ impl Context {
|
||||||
outer: outer.map(Box::new),
|
outer: outer.map(Box::new),
|
||||||
super_classes,
|
super_classes,
|
||||||
super_traits,
|
super_traits,
|
||||||
specializations: vec![],
|
method_defs: vec![],
|
||||||
const_param_defaults: Dict::default(),
|
const_param_defaults: Dict::default(),
|
||||||
method_impl_patches: Dict::default(),
|
method_impl_patches: Dict::default(),
|
||||||
trait_impls: Dict::default(),
|
trait_impls: Dict::default(),
|
||||||
|
@ -539,7 +540,7 @@ impl Context {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn pop(&mut self) -> Result<(), TyCheckErrors> {
|
pub(crate) fn pop(&mut self) -> Result<Context, TyCheckErrors> {
|
||||||
let mut uninited_errs = TyCheckErrors::empty();
|
let mut uninited_errs = TyCheckErrors::empty();
|
||||||
for (name, vi) in self.decls.iter() {
|
for (name, vi) in self.decls.iter() {
|
||||||
uninited_errs.push(TyCheckError::uninitialized_error(
|
uninited_errs.push(TyCheckError::uninitialized_error(
|
||||||
|
@ -551,12 +552,14 @@ impl Context {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
if let Some(parent) = &mut self.outer {
|
if let Some(parent) = &mut self.outer {
|
||||||
*self = mem::take(parent);
|
let parent = mem::take(parent);
|
||||||
|
let ctx = mem::take(self);
|
||||||
|
*self = *parent;
|
||||||
log!(info "{}: current namespace: {}", fn_name!(), self.name);
|
log!(info "{}: current namespace: {}", fn_name!(), self.name);
|
||||||
if !uninited_errs.is_empty() {
|
if !uninited_errs.is_empty() {
|
||||||
Err(uninited_errs)
|
Err(uninited_errs)
|
||||||
} else {
|
} else {
|
||||||
Ok(())
|
Ok(ctx)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Err(TyCheckErrors::from(TyCheckError::checker_bug(
|
Err(TyCheckErrors::from(TyCheckError::checker_bug(
|
||||||
|
|
|
@ -328,13 +328,19 @@ impl Context {
|
||||||
pub(crate) fn preregister(&mut self, block: &ast::Block) -> TyCheckResult<()> {
|
pub(crate) fn preregister(&mut self, block: &ast::Block) -> TyCheckResult<()> {
|
||||||
for expr in block.iter() {
|
for expr in block.iter() {
|
||||||
if let ast::Expr::Def(def) = expr {
|
if let ast::Expr::Def(def) = expr {
|
||||||
|
self.preregister_def(def)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn preregister_def(&mut self, def: &ast::Def) -> TyCheckResult<()> {
|
||||||
let id = Some(def.body.id);
|
let id = Some(def.body.id);
|
||||||
let __name__ = def.sig.ident().map(|i| i.inspect());
|
let __name__ = def.sig.ident().map(|i| i.inspect());
|
||||||
match &def.sig {
|
match &def.sig {
|
||||||
ast::Signature::Subr(sig) => {
|
ast::Signature::Subr(sig) => {
|
||||||
if sig.is_const() {
|
if sig.is_const() {
|
||||||
let (obj, const_t) =
|
let (obj, const_t) = match self.eval_const_block(&def.body.block, __name__) {
|
||||||
match self.eval_const_block(&def.body.block, __name__) {
|
|
||||||
Ok(obj) => (obj.clone(), enum_t(set! {obj})),
|
Ok(obj) => (obj.clone(), enum_t(set! {obj})),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(e);
|
return Err(e);
|
||||||
|
@ -342,13 +348,7 @@ impl Context {
|
||||||
};
|
};
|
||||||
if let Some(spec) = sig.return_t_spec.as_ref() {
|
if let Some(spec) = sig.return_t_spec.as_ref() {
|
||||||
let spec_t = self.instantiate_typespec(spec, PreRegister)?;
|
let spec_t = self.instantiate_typespec(spec, PreRegister)?;
|
||||||
self.sub_unify(
|
self.sub_unify(&const_t, &spec_t, Some(def.body.loc()), None, None)?;
|
||||||
&const_t,
|
|
||||||
&spec_t,
|
|
||||||
Some(def.body.loc()),
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
)?;
|
|
||||||
}
|
}
|
||||||
self.register_const(__name__.unwrap(), obj);
|
self.register_const(__name__.unwrap(), obj);
|
||||||
} else {
|
} else {
|
||||||
|
@ -362,8 +362,7 @@ impl Context {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ast::Signature::Var(sig) if sig.is_const() => {
|
ast::Signature::Var(sig) if sig.is_const() => {
|
||||||
let (obj, const_t) = match self.eval_const_block(&def.body.block, __name__)
|
let (obj, const_t) = match self.eval_const_block(&def.body.block, __name__) {
|
||||||
{
|
|
||||||
Ok(obj) => (obj.clone(), enum_t(set! {obj})),
|
Ok(obj) => (obj.clone(), enum_t(set! {obj})),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Err(e);
|
return Err(e);
|
||||||
|
@ -377,8 +376,6 @@ impl Context {
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ use erg_common::{enum_unwrap, fmt_option, fn_name, get_hash, log, switch_lang, S
|
||||||
use erg_parser::ast;
|
use erg_parser::ast;
|
||||||
use erg_parser::ast::AST;
|
use erg_parser::ast::AST;
|
||||||
|
|
||||||
|
use erg_parser::token::{Token, TokenKind};
|
||||||
use erg_type::constructors::{array, array_mut, class, free_var, func, poly_class, proc, quant};
|
use erg_type::constructors::{array, array_mut, class, free_var, func, poly_class, proc, quant};
|
||||||
use erg_type::free::Constraint;
|
use erg_type::free::Constraint;
|
||||||
use erg_type::typaram::TyParam;
|
use erg_type::typaram::TyParam;
|
||||||
|
@ -338,14 +339,36 @@ impl ASTLowerer {
|
||||||
hir_args.push_kw(hir::KwArg::new(arg.keyword, self.lower_expr(arg.expr)?));
|
hir_args.push_kw(hir::KwArg::new(arg.keyword, self.lower_expr(arg.expr)?));
|
||||||
}
|
}
|
||||||
let obj = self.lower_expr(*call.obj)?;
|
let obj = self.lower_expr(*call.obj)?;
|
||||||
let t = self.ctx.get_call_t(
|
let sig_t = self.ctx.get_call_t(
|
||||||
&obj,
|
&obj,
|
||||||
&call.method_name,
|
&call.method_name,
|
||||||
&hir_args.pos_args,
|
&hir_args.pos_args,
|
||||||
&hir_args.kw_args,
|
&hir_args.kw_args,
|
||||||
&self.ctx.name,
|
&self.ctx.name,
|
||||||
)?;
|
)?;
|
||||||
Ok(hir::Call::new(obj, call.method_name, hir_args, t))
|
Ok(hir::Call::new(obj, call.method_name, hir_args, sig_t))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn lower_pack(&mut self, pack: ast::DataPack) -> LowerResult<hir::Call> {
|
||||||
|
log!(info "entered {}({pack})", fn_name!());
|
||||||
|
let class = self.lower_expr(*pack.class)?;
|
||||||
|
let args = self.lower_record(pack.args)?;
|
||||||
|
let args = vec![hir::PosArg::new(hir::Expr::Record(args))];
|
||||||
|
let method_name = Token::new(
|
||||||
|
TokenKind::Symbol,
|
||||||
|
Str::rc("new"),
|
||||||
|
pack.connector.lineno,
|
||||||
|
pack.connector.col_begin,
|
||||||
|
);
|
||||||
|
let sig_t = self.ctx.get_call_t(
|
||||||
|
&class,
|
||||||
|
&Some(method_name.clone()),
|
||||||
|
&args,
|
||||||
|
&[],
|
||||||
|
&self.ctx.name,
|
||||||
|
)?;
|
||||||
|
let args = hir::Args::new(args, vec![], None);
|
||||||
|
Ok(hir::Call::new(class, Some(method_name), args, sig_t))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TODO: varargs
|
/// TODO: varargs
|
||||||
|
@ -523,6 +546,40 @@ impl ASTLowerer {
|
||||||
Ok(hir::Def::new(hir::Signature::Subr(sig), body))
|
Ok(hir::Def::new(hir::Signature::Subr(sig), body))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn lower_method_defs(&mut self, method_defs: ast::MethodDefs) -> LowerResult<hir::MethodDefs> {
|
||||||
|
log!(info "entered {}({method_defs})", fn_name!());
|
||||||
|
let mut hir_defs = hir::RecordAttrs::new();
|
||||||
|
let class = self
|
||||||
|
.ctx
|
||||||
|
.instantiate_typespec(&method_defs.class, RegistrationMode::Normal)?;
|
||||||
|
self.ctx
|
||||||
|
.grow(&class.name(), ContextKind::MethodDefs, Private)?;
|
||||||
|
for def in method_defs.defs.iter() {
|
||||||
|
self.ctx.preregister_def(def)?;
|
||||||
|
}
|
||||||
|
for def in method_defs.defs.into_iter() {
|
||||||
|
let def = self.lower_def(def)?;
|
||||||
|
hir_defs.push(def);
|
||||||
|
}
|
||||||
|
match self.ctx.pop() {
|
||||||
|
Ok(ctx) => {
|
||||||
|
if let Some((_, class_root)) = self.ctx.poly_types.get_mut(&class.name()) {
|
||||||
|
class_root.method_defs.push((class, ctx));
|
||||||
|
} else {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(mut errs) => {
|
||||||
|
self.errs.append(&mut errs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(hir::MethodDefs::new(
|
||||||
|
method_defs.class,
|
||||||
|
method_defs.vis,
|
||||||
|
hir_defs,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
// Call.obj == Accessor cannot be type inferred by itself (it can only be inferred with arguments)
|
// Call.obj == Accessor cannot be type inferred by itself (it can only be inferred with arguments)
|
||||||
// so turn off type checking (check=false)
|
// so turn off type checking (check=false)
|
||||||
fn lower_expr(&mut self, expr: ast::Expr) -> LowerResult<hir::Expr> {
|
fn lower_expr(&mut self, expr: ast::Expr) -> LowerResult<hir::Expr> {
|
||||||
|
@ -536,8 +593,10 @@ impl ASTLowerer {
|
||||||
ast::Expr::BinOp(bin) => Ok(hir::Expr::BinOp(self.lower_bin(bin)?)),
|
ast::Expr::BinOp(bin) => Ok(hir::Expr::BinOp(self.lower_bin(bin)?)),
|
||||||
ast::Expr::UnaryOp(unary) => Ok(hir::Expr::UnaryOp(self.lower_unary(unary)?)),
|
ast::Expr::UnaryOp(unary) => Ok(hir::Expr::UnaryOp(self.lower_unary(unary)?)),
|
||||||
ast::Expr::Call(call) => Ok(hir::Expr::Call(self.lower_call(call)?)),
|
ast::Expr::Call(call) => Ok(hir::Expr::Call(self.lower_call(call)?)),
|
||||||
|
ast::Expr::DataPack(pack) => Ok(hir::Expr::Call(self.lower_pack(pack)?)),
|
||||||
ast::Expr::Lambda(lambda) => Ok(hir::Expr::Lambda(self.lower_lambda(lambda)?)),
|
ast::Expr::Lambda(lambda) => Ok(hir::Expr::Lambda(self.lower_lambda(lambda)?)),
|
||||||
ast::Expr::Def(def) => Ok(hir::Expr::Def(self.lower_def(def)?)),
|
ast::Expr::Def(def) => Ok(hir::Expr::Def(self.lower_def(def)?)),
|
||||||
|
ast::Expr::MethodDefs(defs) => Ok(hir::Expr::MethodDefs(self.lower_method_defs(defs)?)),
|
||||||
other => todo!("{other}"),
|
other => todo!("{other}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -897,6 +897,7 @@ impl Call {
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
pub struct DataPack {
|
pub struct DataPack {
|
||||||
pub class: Box<Expr>,
|
pub class: Box<Expr>,
|
||||||
|
pub connector: Token,
|
||||||
pub args: Record,
|
pub args: Record,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -915,9 +916,10 @@ impl Locational for DataPack {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DataPack {
|
impl DataPack {
|
||||||
pub fn new(class: Expr, args: Record) -> Self {
|
pub fn new(class: Expr, connector: Token, args: Record) -> Self {
|
||||||
Self {
|
Self {
|
||||||
class: Box::new(class),
|
class: Box::new(class),
|
||||||
|
connector,
|
||||||
args,
|
args,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -330,7 +330,7 @@ impl Desugarer {
|
||||||
let class = self.rec_desugar_shortened_record(*pack.class);
|
let class = self.rec_desugar_shortened_record(*pack.class);
|
||||||
let rec = self.desugar_shortened_record_inner(rec);
|
let rec = self.desugar_shortened_record_inner(rec);
|
||||||
let args = Record::Normal(rec);
|
let args = Record::Normal(rec);
|
||||||
Expr::DataPack(DataPack::new(class, args))
|
Expr::DataPack(DataPack::new(class, pack.connector, args))
|
||||||
} else {
|
} else {
|
||||||
Expr::DataPack(pack)
|
Expr::DataPack(pack)
|
||||||
}
|
}
|
||||||
|
|
|
@ -997,7 +997,7 @@ impl Parser {
|
||||||
.map_err(|_| self.stack_dec())?;
|
.map_err(|_| self.stack_dec())?;
|
||||||
match container {
|
match container {
|
||||||
BraceContainer::Record(args) => {
|
BraceContainer::Record(args) => {
|
||||||
let pack = DataPack::new(maybe_class, args);
|
let pack = DataPack::new(maybe_class, vis, args);
|
||||||
stack.push(ExprOrOp::Expr(Expr::DataPack(pack)));
|
stack.push(ExprOrOp::Expr(Expr::DataPack(pack)));
|
||||||
}
|
}
|
||||||
BraceContainer::Dict(dict) => todo!("{dict}"),
|
BraceContainer::Dict(dict) => todo!("{dict}"),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue