Implement subtype ascription

This commit is contained in:
Shunsuke Shibayama 2022-10-19 19:04:50 +09:00
parent 6613fd85c2
commit cb4c2c7bbf
5 changed files with 120 additions and 48 deletions

View file

@ -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(

View file

@ -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) {

View file

@ -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

View file

@ -1413,6 +1413,7 @@ impl ASTLowerer {
fn lower_type_asc(&mut self, tasc: ast::TypeAscription) -> LowerResult<hir::TypeAscription> {
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)?;
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<hir::TypeAscription> {
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<hir::Expr> {
log!(info "entered {}", fn_name!());
match expr {

View file

@ -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)]