diff --git a/compiler/erg_compiler/codegen.rs b/compiler/erg_compiler/codegen.rs index 1807d6fa..49754765 100644 --- a/compiler/erg_compiler/codegen.rs +++ b/compiler/erg_compiler/codegen.rs @@ -2439,6 +2439,69 @@ impl PyCodeGenerator { debug_assert_eq!(self.stack_len(), init_stack_len + 1); } + // TODO: tuple comprehension + // TODO: tuples can be const + fn emit_tuple(&mut self, tuple: Tuple) { + match tuple { + Tuple::Normal(mut tup) => { + let len = tup.elems.len(); + while let Some(arg) = tup.elems.try_remove_pos(0) { + self.emit_expr(arg.expr); + } + self.write_instr(BUILD_TUPLE); + self.write_arg(len); + if len == 0 { + self.stack_inc(); + } else { + self.stack_dec_n(len - 1); + } + } + } + } + + fn emit_set(&mut self, set: crate::hir::Set) { + match set { + crate::hir::Set::Normal(mut set) => { + let len = set.elems.len(); + while let Some(arg) = set.elems.try_remove_pos(0) { + self.emit_expr(arg.expr); + } + self.write_instr(BUILD_SET); + self.write_arg(len); + if len == 0 { + self.stack_inc(); + } else { + self.stack_dec_n(len - 1); + } + } + crate::hir::Set::WithLength(st) => { + self.emit_expr(*st.elem); + self.write_instr(BUILD_SET); + self.write_arg(1); + } + } + } + + fn emit_dict(&mut self, dict: crate::hir::Dict) { + match dict { + crate::hir::Dict::Normal(dic) => { + let len = dic.kvs.len(); + for kv in dic.kvs.into_iter() { + self.emit_expr(kv.key); + self.emit_expr(kv.value); + } + self.write_instr(BUILD_MAP); + self.write_arg(len); + if len == 0 { + self.stack_inc(); + } else { + self.stack_dec_n(2 * len - 1); + } + } + other => todo!("{other}"), + } + } + #[allow(clippy::identity_op)] fn emit_record(&mut self, rec: Record) { log!(info "entered {} ({rec})", fn_name!()); @@ -2505,8 +2568,30 @@ impl PyCodeGenerator { self.stack_dec(); } - fn emit_expr(&mut self, expr: Expr) { - log!(info "entered {} ({expr})", fn_name!()); + fn emit_compound(&mut self, chunks: Block) { + let is_module_loading_chunks = chunks + .get(2) + .map(|chunk| { + option_enum_unwrap!(chunk, Expr::Call) + .map(|call| call.obj.show_acc().as_ref().map(|s| &s[..]) == Some("exec")) + .unwrap_or(false) + }) + .unwrap_or(false); + if !self.module_type_loaded && is_module_loading_chunks { + self.load_module_type(); + self.module_type_loaded = true; + } + let init_stack_len = self.stack_len(); + for chunk in chunks.into_iter() { + self.emit_chunk(chunk); + if self.stack_len() == init_stack_len + 1 { + self.emit_pop_top(); + } + } + self.cancel_if_pop_top(); + } + + fn push_lnotab(&mut self, expr: &Expr) { if expr.ln_begin().unwrap_or_else(|| panic!("{expr}")) > self.cur_block().prev_lineno { let sd = self.lasti() - self.cur_block().prev_lasti; let ld = expr.ln_begin().unwrap() - self.cur_block().prev_lineno; @@ -2538,6 +2623,40 @@ impl PyCodeGenerator { self.crash("codegen failed: invalid bytecode format"); } } + } + + fn emit_chunk(&mut self, chunk: Expr) { + log!(info "entered {} ({chunk})", fn_name!()); + self.push_lnotab(&chunk); + match chunk { + Expr::Lit(lit) => self.emit_load_const(lit.value), + Expr::Accessor(acc) => self.emit_acc(acc), + Expr::Def(def) => self.emit_def(def), + Expr::ClassDef(class) => self.emit_class_def(class), + Expr::PatchDef(patch) => self.emit_patch_def(patch), + Expr::AttrDef(attr) => self.emit_attr_def(attr), + Expr::Lambda(lambda) => self.emit_lambda(lambda), + Expr::UnaryOp(unary) => self.emit_unaryop(unary), + Expr::BinOp(bin) => self.emit_binop(bin), + Expr::Call(call) => self.emit_call(call), + Expr::Array(arr) => self.emit_array(arr), + Expr::Tuple(tup) => self.emit_tuple(tup), + Expr::Set(set) => self.emit_set(set), + Expr::Dict(dict) => self.emit_dict(dict), + Expr::Record(rec) => self.emit_record(rec), + Expr::Code(code) => { + let code = self.emit_block(code, None, vec![]); + self.emit_load_const(code); + } + Expr::Compound(chunks) => self.emit_compound(chunks), + Expr::Import(acc) => self.emit_import(acc), + Expr::Dummy(_) | Expr::TypeAsc(_) => {} + } + } + + fn emit_expr(&mut self, expr: Expr) { + log!(info "entered {} ({expr})", fn_name!()); + self.push_lnotab(&expr); match expr { Expr::Lit(lit) => self.emit_load_const(lit.value), Expr::Accessor(acc) => self.emit_acc(acc), @@ -2550,92 +2669,16 @@ impl PyCodeGenerator { Expr::BinOp(bin) => self.emit_binop(bin), Expr::Call(call) => self.emit_call(call), Expr::Array(arr) => self.emit_array(arr), - // TODO: tuple comprehension - // TODO: tuples can be const - Expr::Tuple(tup) => match tup { - Tuple::Normal(mut tup) => { - let len = tup.elems.len(); - while let Some(arg) = tup.elems.try_remove_pos(0) { - self.emit_expr(arg.expr); - } - self.write_instr(BUILD_TUPLE); - self.write_arg(len); - if len == 0 { - self.stack_inc(); - } else { - self.stack_dec_n(len - 1); - } - } - }, - Expr::Set(set) => match set { - crate::hir::Set::Normal(mut set) => { - let len = set.elems.len(); - while let Some(arg) = set.elems.try_remove_pos(0) { - self.emit_expr(arg.expr); - } - self.write_instr(BUILD_SET); - self.write_arg(len); - if len == 0 { - self.stack_inc(); - } else { - self.stack_dec_n(len - 1); - } - } - crate::hir::Set::WithLength(st) => { - self.emit_expr(*st.elem); - self.write_instr(BUILD_SET); - self.write_arg(1); - } - }, - Expr::Dict(dict) => match dict { - crate::hir::Dict::Normal(dic) => { - let len = dic.kvs.len(); - for kv in dic.kvs.into_iter() { - self.emit_expr(kv.key); - self.emit_expr(kv.value); - } - self.write_instr(BUILD_MAP); - self.write_arg(len); - if len == 0 { - self.stack_inc(); - } else { - self.stack_dec_n(2 * len - 1); - } - } - other => todo!("{other}"), - }, + Expr::Tuple(tup) => self.emit_tuple(tup), + Expr::Set(set) => self.emit_set(set), + Expr::Dict(dict) => self.emit_dict(dict), Expr::Record(rec) => self.emit_record(rec), Expr::Code(code) => { let code = self.emit_block(code, None, vec![]); self.emit_load_const(code); } - Expr::Compound(chunks) => { - let is_module_loading_chunks = chunks - .get(2) - .map(|chunk| { - option_enum_unwrap!(chunk, Expr::Call) - .map(|call| { - call.obj.show_acc().as_ref().map(|s| &s[..]) == Some("exec") - }) - .unwrap_or(false) - }) - .unwrap_or(false); - if !self.module_type_loaded && is_module_loading_chunks { - self.load_module_type(); - self.module_type_loaded = true; - } - let init_stack_len = self.stack_len(); - for expr in chunks.into_iter() { - self.emit_expr(expr); - if self.stack_len() == init_stack_len + 1 { - self.emit_pop_top(); - } - } - self.cancel_if_pop_top(); - } - Expr::TypeAsc(tasc) => { - self.emit_expr(*tasc.expr); - } + Expr::Compound(chunks) => self.emit_compound(chunks), + Expr::TypeAsc(tasc) => self.emit_expr(*tasc.expr), Expr::Import(acc) => self.emit_import(acc), Expr::Dummy(_) => {} } @@ -2649,8 +2692,8 @@ impl PyCodeGenerator { self.emit_store_instr(Identifier::public_with_line(DOT, param, line), Name); } let init_stack_len = self.stack_len(); - for expr in block.into_iter() { - self.emit_expr(expr); + for chunk in block.into_iter() { + self.emit_chunk(chunk); if self.stack_len() > init_stack_len { self.emit_pop_top(); } @@ -2665,8 +2708,8 @@ impl PyCodeGenerator { self.emit_store_instr(Identifier::public_with_line(DOT, param, line), Name); } let init_stack_len = self.stack_len(); - for expr in block.into_iter() { - self.emit_expr(expr); + for chunk in block.into_iter() { + self.emit_chunk(chunk); // __exit__, __enter__() are on the stack if self.stack_len() > init_stack_len { self.emit_pop_top(); @@ -2883,8 +2926,8 @@ impl PyCodeGenerator { 0 }; let init_stack_len = self.stack_len(); - for expr in block.into_iter() { - self.emit_expr(expr); + for chunk in block.into_iter() { + self.emit_chunk(chunk); // NOTE: 各行のトップレベルでは0個または1個のオブジェクトが残っている // Pythonの場合使わなかったオブジェクトはそのまま捨てられるが、Ergではdiscardを使う必要がある // TODO: discard @@ -3083,8 +3126,8 @@ impl PyCodeGenerator { if !self.cfg.no_std && !self.prelude_loaded { self.load_prelude(); } - for expr in hir.module.into_iter() { - self.emit_expr(expr); + for chunk in hir.module.into_iter() { + self.emit_chunk(chunk); // TODO: discard if self.stack_len() == 1 { self.emit_pop_top(); diff --git a/compiler/erg_compiler/context/inquire.rs b/compiler/erg_compiler/context/inquire.rs index bae67d63..c8ca322a 100644 --- a/compiler/erg_compiler/context/inquire.rs +++ b/compiler/erg_compiler/context/inquire.rs @@ -403,7 +403,11 @@ impl Context { input: &Input, namespace: &Str, ) -> SingleTyCheckResult { - if let Some(vi) = self.decls.get(&ident.inspect()[..]) { + if let Some(vi) = self + .decls + .get(&ident.inspect()[..]) + .or_else(|| self.future_defined_locals.get(&ident.inspect()[..])) + { match self.validate_visibility(ident, vi, input, namespace) { Ok(()) => { return Ok(vi.clone()); diff --git a/compiler/erg_compiler/lower.rs b/compiler/erg_compiler/lower.rs index 7f13f603..8dd4170b 100644 --- a/compiler/erg_compiler/lower.rs +++ b/compiler/erg_compiler/lower.rs @@ -1844,6 +1844,70 @@ impl ASTLowerer { Ok(expr.type_asc(tasc.t_spec)) } + fn lower_decl(&mut self, tasc: ast::TypeAscription) -> LowerResult { + log!(info "entered {}({tasc})", fn_name!()); + let is_instance_ascription = tasc.is_instance_ascription(); + let mut dummy_tv_cache = TyVarCache::new(self.ctx.level, &self.ctx); + let spec_t = self.ctx.instantiate_typespec( + &tasc.t_spec, + None, + &mut dummy_tv_cache, + RegistrationMode::Normal, + false, + )?; + let loc = tasc.loc(); + let ast::Expr::Accessor(ast::Accessor::Ident(ident)) = *tasc.expr else { + return Err(LowerErrors::from(LowerError::syntax_error( + self.cfg.input.clone(), + line!() as usize, + tasc.expr.loc(), + self.ctx.caused_by(), + switch_lang!( + "japanese" => "無効な型宣言です".to_string(), + "simplified_chinese" => "无效的类型声明".to_string(), + "traditional_chinese" => "無效的型宣告".to_string(), + "english" => "Invalid type declaration".to_string(), + ), + None, + ))); + }; + let ident_vi = self + .ctx + .rec_get_decl_info(&ident, AccessKind::Name, &self.cfg.input, &self.ctx.name) + .or_else(|_e| { + self.ctx + .rec_get_var_info(&ident, AccessKind::Name, &self.cfg.input, &self.ctx.name) + })?; + if is_instance_ascription { + self.ctx + .sub_unify(&ident_vi.t, &spec_t, loc, Some(ident.inspect()))?; + } else { + // if subtype ascription + let ctx = self.ctx.get_singular_ctx_by_ident(&ident, &self.ctx.name)?; + // REVIEW: need to use subtype_of? + if ctx.super_traits.iter().all(|trait_| trait_ != &spec_t) + && ctx.super_classes.iter().all(|class| class != &spec_t) + { + return Err(LowerErrors::from(LowerError::subtyping_error( + self.cfg.input.clone(), + line!() as usize, + &ident_vi.t, + &spec_t, + loc, + self.ctx.caused_by(), + ))); + } + } + let qual_name = self + .ctx + .get_singular_ctx_by_ident(&ident, &self.ctx.name) + .ok() + .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)); + Ok(expr.type_asc(tasc.t_spec)) + } + // Call.obj == Accessor cannot be type inferred by itself (it can only be inferred with arguments) // so turn off type checking (check=false) fn lower_expr(&mut self, expr: ast::Expr) -> LowerResult { @@ -1861,14 +1925,28 @@ impl ASTLowerer { 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::TypeAsc(tasc) => Ok(hir::Expr::TypeAsc(self.lower_type_asc(tasc)?)), + // Checking is also performed for expressions in Dummy. However, it has no meaning in code generation + ast::Expr::Dummy(dummy) => Ok(hir::Expr::Dummy(self.lower_dummy(dummy)?)), + other => { + log!(err "unreachable: {other}"); + unreachable_error!(LowerErrors, LowerError, self.ctx) + } + } + } + + /// The meaning of TypeAscription changes between chunk and expr. + /// For example, `x: Int`, as expr, is `x` itself, + /// but as chunk, it declares that `x` is of type `Int`, and is valid even before `x` is defined. + fn lower_chunk(&mut self, chunk: ast::Expr) -> LowerResult { + log!(info "entered {}", fn_name!()); + match chunk { ast::Expr::Def(def) => Ok(hir::Expr::Def(self.lower_def(def)?)), ast::Expr::ClassDef(defs) => Ok(hir::Expr::ClassDef(self.lower_class_def(defs)?)), ast::Expr::PatchDef(defs) => Ok(hir::Expr::PatchDef(self.lower_patch_def(defs)?)), ast::Expr::AttrDef(adef) => Ok(hir::Expr::AttrDef(self.lower_attr_def(adef)?)), - ast::Expr::TypeAsc(tasc) => Ok(hir::Expr::TypeAsc(self.lower_type_asc(tasc)?)), - // Checking is also performed for expressions in Dummy. However, it has no meaning in code generation - ast::Expr::Dummy(dummy) => Ok(hir::Expr::Dummy(self.lower_dummy(dummy)?)), - other => todo!("{other}"), + ast::Expr::TypeAsc(tasc) => Ok(hir::Expr::TypeAsc(self.lower_decl(tasc)?)), + other => self.lower_expr(other), } } @@ -1876,7 +1954,7 @@ impl ASTLowerer { log!(info "entered {}", fn_name!()); let mut hir_block = Vec::with_capacity(ast_block.len()); for chunk in ast_block.into_iter() { - let chunk = self.lower_expr(chunk)?; + let chunk = self.lower_chunk(chunk)?; hir_block.push(chunk); } Ok(hir::Block::new(hir_block)) @@ -1886,7 +1964,7 @@ impl ASTLowerer { log!(info "entered {}", fn_name!()); let mut hir_dummy = Vec::with_capacity(ast_dummy.len()); for chunk in ast_dummy.into_iter() { - let chunk = self.lower_expr(chunk)?; + let chunk = self.lower_chunk(chunk)?; hir_dummy.push(chunk); } Ok(hir::Dummy::new(hir_dummy)) @@ -1928,7 +2006,7 @@ impl ASTLowerer { self.errs.extend(errs); } for chunk in ast.module.into_iter() { - match self.lower_expr(chunk) { + match self.lower_chunk(chunk) { Ok(chunk) => { module.push(chunk); }