From cb4c2c7bbfa989f5ed1a65a44188ccf4936da3fe Mon Sep 17 00:00:00 2001 From: Shunsuke Shibayama Date: Wed, 19 Oct 2022 19:04:50 +0900 Subject: [PATCH] Implement subtype ascription --- .../erg_compiler/context/initialize/mod.rs | 10 ++ compiler/erg_compiler/context/inquire.rs | 2 +- compiler/erg_compiler/lib/pystd/tarfile.d.er | 4 +- compiler/erg_compiler/lower.rs | 144 ++++++++++++------ compiler/erg_parser/ast.rs | 8 + 5 files changed, 120 insertions(+), 48 deletions(-) diff --git a/compiler/erg_compiler/context/initialize/mod.rs b/compiler/erg_compiler/context/initialize/mod.rs index ffa024ad..2e58e261 100644 --- a/compiler/erg_compiler/context/initialize/mod.rs +++ b/compiler/erg_compiler/context/initialize/mod.rs @@ -410,6 +410,12 @@ impl Context { set! { subtypeof(mono_q("Self"), mono("Writable!")) }, ); writable.register_builtin_py_decl("write!", t_write, Public, Some("write")); + // TODO: Add required methods + let mut filelike = Self::builtin_mono_trait("FileLike", 2); + filelike.register_superclass(mono("Readable"), &readable); + let mut filelike_mut = Self::builtin_mono_trait("FileLike!", 2); + filelike_mut.register_superclass(mono("FileLike"), &filelike); + filelike_mut.register_superclass(mono("Writable!"), &writable); /* Show */ let mut show = Self::builtin_mono_trait("Show", 2); let t_show = fn0_met(ref_(mono_q("Self")), Str); @@ -568,6 +574,8 @@ impl Context { Const, Some("Writable"), ); + self.register_builtin_type(mono("FileLike"), filelike, Private, Const, None); + self.register_builtin_type(mono("FileLike!"), filelike_mut, Private, Const, None); self.register_builtin_type(mono("Show"), show, Private, Const, None); self.register_builtin_type( poly("Input", vec![ty_tp(mono_q("T"))]), @@ -1385,6 +1393,8 @@ impl Context { Some("write"), ); file_mut.register_trait(mono("File!"), file_mut_writable); + file_mut.register_marker_trait(mono("FileLike")); + file_mut.register_marker_trait(mono("FileLike!")); /* Array_mut */ let array_mut_t = poly("Array!", vec![ty_tp(mono_q("T")), mono_q_tp("N")]); let mut array_mut_ = Self::builtin_poly_class( diff --git a/compiler/erg_compiler/context/inquire.rs b/compiler/erg_compiler/context/inquire.rs index 427bae86..7a78be17 100644 --- a/compiler/erg_compiler/context/inquire.rs +++ b/compiler/erg_compiler/context/inquire.rs @@ -1870,7 +1870,7 @@ impl Context { } } - fn get_mut_type(&mut self, name: &str) -> Option<(&Type, &mut Context)> { + pub(crate) fn get_mut_type(&mut self, name: &str) -> Option<(&Type, &mut Context)> { if let Some((t, ctx)) = self.mono_types.get_mut(name) { Some((t, ctx)) } else if let Some((t, ctx)) = self.poly_types.get_mut(name) { diff --git a/compiler/erg_compiler/lib/pystd/tarfile.d.er b/compiler/erg_compiler/lib/pystd/tarfile.d.er index 8de2091c..bbb7c839 100644 --- a/compiler/erg_compiler/lib/pystd/tarfile.d.er +++ b/compiler/erg_compiler/lib/pystd/tarfile.d.er @@ -1,4 +1,6 @@ .TarFile!: ClassType +.TarFile! <: FileLike +.TarFile! <: FileLike! .TarFile!.open!: (path: PathLike or NoneType := NoneType, mode := Str) => .TarFile! .TarFile!.add!: (self: RefMut(.TarFile!), name: PathLike, arcname: PathLike or NoneType := NoneType, recursive := Bool) => NoneType .TarFile!.close!: (self: .TarFile!,) => NoneType @@ -6,5 +8,5 @@ .TarFile!.getnames: (self: Ref(.TarFile!),) -> [Str; _] .TarFile!.list: (self: Ref(.TarFile!), verbose := Bool) -> [Str; _] -.open!: (path: PathLike or NoneType := NoneType, mode := Str) => .TarFile! +.open!: (path: PathLike or NoneType := NoneType, mode := Str, fileobj: FileLike or NoneType := NoneType) => .TarFile! .is_tarfile: (name: Str or File!,) -> Bool diff --git a/compiler/erg_compiler/lower.rs b/compiler/erg_compiler/lower.rs index 9c7ea655..5b7c1787 100644 --- a/compiler/erg_compiler/lower.rs +++ b/compiler/erg_compiler/lower.rs @@ -1413,6 +1413,7 @@ impl ASTLowerer { fn lower_type_asc(&mut self, tasc: ast::TypeAscription) -> LowerResult { log!(info "entered {}({tasc})", fn_name!()); + let is_instance_ascription = tasc.is_instance_ascription(); let t = self.ctx.instantiate_typespec( &tasc.t_spec, None, @@ -1420,13 +1421,33 @@ impl ASTLowerer { RegistrationMode::Normal, false, )?; + let loc = tasc.loc(); let expr = self.lower_expr(*tasc.expr)?; - self.ctx.sub_unify( - expr.ref_t(), - &t, - expr.loc(), - Some(&Str::from(expr.to_string())), - )?; + if is_instance_ascription { + self.ctx.sub_unify( + expr.ref_t(), + &t, + expr.loc(), + Some(&Str::from(expr.to_string())), + )?; + } else { + let ctx = self + .ctx + .get_singular_ctx_by_hir_expr(&expr, &self.ctx.name)?; + // REVIEW: need to use subtype_of? + if ctx.super_traits.iter().all(|trait_| trait_ != &t) + && ctx.super_classes.iter().all(|class| class != &t) + { + return Err(LowerErrors::from(LowerError::subtyping_error( + self.cfg.input.clone(), + line!() as usize, + expr.ref_t(), // FIXME: + &t, + loc, + self.ctx.caused_by(), + ))); + } + } Ok(expr.type_asc(tasc.t_spec)) } @@ -1559,6 +1580,7 @@ impl ASTLowerer { fn declare_ident(&mut self, tasc: ast::TypeAscription) -> LowerResult { log!(info "entered {}({})", fn_name!(), tasc); + let is_instance_ascription = tasc.is_instance_ascription(); match *tasc.expr { ast::Expr::Accessor(ast::Accessor::Ident(ident)) => { let py_name = Str::rc(ident.inspect().trim_end_matches('!')); @@ -1569,46 +1591,10 @@ impl ASTLowerer { RegistrationMode::Normal, false, )?; - if ident.is_const() { - let vi = VarInfo::new( - t.clone(), - Mutability::Const, - ident.vis(), - VarKind::Declared, - None, - None, - Some(py_name.clone()), - ); - self.ctx.decls.insert(ident.name.clone(), vi); - } - self.ctx.assign_var_sig( - &ast::VarSignature::new(ast::VarPattern::Ident(ident.clone()), None), - &t, - ast::DefId(0), - Some(py_name), - )?; - match t { - Type::ClassType => { - let ty_obj = GenTypeObj::new( - TypeKind::Class, - mono(format!("{}{ident}", self.ctx.path())), - TypeObj::Builtin(Type::Uninited), - None, - None, - ); - self.ctx.register_gen_type(&ident, ty_obj); - } - Type::TraitType => { - let ty_obj = GenTypeObj::new( - TypeKind::Trait, - mono(format!("{}{ident}", self.ctx.path())), - TypeObj::Builtin(Type::Uninited), - None, - None, - ); - self.ctx.register_gen_type(&ident, ty_obj); - } - _ => {} + if is_instance_ascription { + self.declare_instance(&ident, &t, py_name)?; + } else { + self.declare_subtype(&ident, &t)?; } let muty = Mutability::from(&ident.inspect()[..]); let vis = ident.vis(); @@ -1654,6 +1640,72 @@ impl ASTLowerer { } } + fn declare_instance( + &mut self, + ident: &ast::Identifier, + t: &Type, + py_name: Str, + ) -> LowerResult<()> { + if ident.is_const() { + let vi = VarInfo::new( + t.clone(), + Mutability::Const, + ident.vis(), + VarKind::Declared, + None, + None, + Some(py_name.clone()), + ); + self.ctx.decls.insert(ident.name.clone(), vi); + } + self.ctx.assign_var_sig( + &ast::VarSignature::new(ast::VarPattern::Ident(ident.clone()), None), + t, + ast::DefId(0), + Some(py_name), + )?; + match t { + Type::ClassType => { + let ty_obj = GenTypeObj::new( + TypeKind::Class, + mono(format!("{}{ident}", self.ctx.path())), + TypeObj::Builtin(Type::Uninited), + None, + None, + ); + self.ctx.register_gen_type(ident, ty_obj); + } + Type::TraitType => { + let ty_obj = GenTypeObj::new( + TypeKind::Trait, + mono(format!("{}{ident}", self.ctx.path())), + TypeObj::Builtin(Type::Uninited), + None, + None, + ); + self.ctx.register_gen_type(ident, ty_obj); + } + _ => {} + } + Ok(()) + } + + fn declare_subtype(&mut self, ident: &ast::Identifier, trait_: &Type) -> LowerResult<()> { + if let Some((_, ctx)) = self.ctx.get_mut_type(ident.inspect()) { + ctx.register_marker_trait(trait_.clone()); + Ok(()) + } else { + Err(LowerErrors::from(LowerError::no_var_error( + self.cfg.input.clone(), + line!() as usize, + ident.loc(), + self.ctx.caused_by(), + ident.inspect(), + self.ctx.get_similar_name(ident.inspect()), + ))) + } + } + fn declare_chunk(&mut self, expr: ast::Expr) -> LowerResult { log!(info "entered {}", fn_name!()); match expr { diff --git a/compiler/erg_parser/ast.rs b/compiler/erg_parser/ast.rs index 7fe9718d..0987c4b0 100644 --- a/compiler/erg_parser/ast.rs +++ b/compiler/erg_parser/ast.rs @@ -3011,6 +3011,14 @@ impl TypeAscription { t_spec, } } + + pub fn is_instance_ascription(&self) -> bool { + self.op.is(TokenKind::Colon) + } + + pub fn is_subtype_ascription(&self) -> bool { + self.op.is(TokenKind::SubtypeOf) + } } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]