Implement assert casting

This commit is contained in:
Shunsuke Shibayama 2022-10-09 00:14:50 +09:00
parent 4e70e2e980
commit de1180387c
13 changed files with 252 additions and 51 deletions

View file

@ -1129,7 +1129,7 @@ impl CodeGenerator {
CompileError::feature_error( CompileError::feature_error(
self.cfg.input.clone(), self.cfg.input.clone(),
unary.op.loc(), unary.op.loc(),
"", &unary.op.inspect().clone(),
AtomicStr::from(unary.op.content), AtomicStr::from(unary.op.content),
) )
.write_to_stderr(); .write_to_stderr();
@ -1177,7 +1177,7 @@ impl CodeGenerator {
CompileError::feature_error( CompileError::feature_error(
self.cfg.input.clone(), self.cfg.input.clone(),
bin.op.loc(), bin.op.loc(),
"", &bin.op.inspect().clone(),
AtomicStr::from(bin.op.content), AtomicStr::from(bin.op.content),
) )
.write_to_stderr(); .write_to_stderr();
@ -1739,7 +1739,7 @@ impl CodeGenerator {
} }
// Dict, // Dict,
other => { other => {
CompileError::feature_error(self.cfg.input.clone(), other.loc(), "???", "".into()) CompileError::feature_error(self.cfg.input.clone(), other.loc(), "Dict", "".into())
.write_to_stderr(); .write_to_stderr();
self.crash("cannot compile this expression at this time"); self.crash("cannot compile this expression at this time");
} }

View file

@ -681,25 +681,26 @@ impl Context {
pub(crate) fn cyclic_supertype_of(&self, lhs: &FreeTyVar, rhs: &Type) -> bool { pub(crate) fn cyclic_supertype_of(&self, lhs: &FreeTyVar, rhs: &Type) -> bool {
let subst_ctx = SubstContext::new(rhs, self, Location::Unknown); let subst_ctx = SubstContext::new(rhs, self, Location::Unknown);
if let Some(super_traits) = self.get_nominal_type_ctx(rhs).map(|ctx| &ctx.super_traits) { if let Some(super_traits) = self.get_nominal_type_ctx(rhs).map(|ctx| &ctx.super_traits) {
for sup_trait in super_traits { for super_trait in super_traits {
let sup_trait = if sup_trait.has_qvar() { let sup_trait = if super_trait.has_qvar() {
subst_ctx.substitute(sup_trait.clone()).unwrap() subst_ctx.substitute(super_trait.clone()).unwrap()
} else { } else {
sup_trait.clone() super_trait.clone()
}; };
if self.sup_conforms(lhs, rhs, &sup_trait) { if self.sup_conforms(lhs, rhs, &sup_trait) {
return true; return true;
} }
} }
} }
if let Some(sup_classes) = self.get_nominal_type_ctx(rhs).map(|ctx| &ctx.super_classes) { if let Some(super_classes) = self.get_super_classes(rhs) {
for sup_class in sup_classes { for super_class in super_classes {
let sup_class = if sup_class.has_qvar() { let sup_class = if super_class.has_qvar() {
subst_ctx.substitute(sup_class.clone()).unwrap() subst_ctx.substitute(super_class).unwrap()
} else { } else {
sup_class.clone() super_class
}; };
if self.cyclic_supertype_of(lhs, &sup_class) { if self.cyclic_supertype_of(lhs, &sup_class) {
log!(err "引っかかった: {lhs}, {sup_class}");
return true; return true;
} }
} }

View file

@ -469,10 +469,11 @@ impl Context {
); );
let mut obj_in = Self::builtin_methods("In", 2); let mut obj_in = Self::builtin_methods("In", 2);
obj_in.register_builtin_impl("__in__", fn1_met(Obj, Type, Bool), Const, Public); obj_in.register_builtin_impl("__in__", fn1_met(Obj, Type, Bool), Const, Public);
obj.register_trait(Obj, builtin_poly("Eq", vec![ty_tp(Type)]), obj_in); obj.register_trait(Obj, builtin_poly("In", vec![ty_tp(Type)]), obj_in);
let mut obj_mutizable = Self::builtin_methods("Mutizable", 1); let mut obj_mutizable = Self::builtin_methods("Mutizable", 1);
obj_mutizable.register_builtin_const("MutType!", ValueObj::builtin_t(builtin_mono("Obj!"))); obj_mutizable.register_builtin_const("MutType!", ValueObj::builtin_t(builtin_mono("Obj!")));
obj.register_trait(Obj, builtin_mono("Mutizable"), obj_mutizable); obj.register_trait(Obj, builtin_mono("Mutizable"), obj_mutizable);
// Obj does not implement Eq
/* Float */ /* Float */
let mut float = Self::builtin_mono_class("Float", 2); let mut float = Self::builtin_mono_class("Float", 2);
@ -760,6 +761,24 @@ impl Context {
let mut str_show = Self::builtin_methods("Show", 1); let mut str_show = Self::builtin_methods("Show", 1);
str_show.register_builtin_impl("to_str", fn0_met(Str, Str), Immutable, Public); str_show.register_builtin_impl("to_str", fn0_met(Str, Str), Immutable, Public);
str_.register_trait(Str, builtin_mono("Show"), str_show); str_.register_trait(Str, builtin_mono("Show"), str_show);
/* NoneType */
let mut nonetype = Self::builtin_mono_class("NoneType", 10);
nonetype.register_superclass(Obj, &obj);
let mut nonetype_eq = Self::builtin_methods("Eq", 2);
nonetype_eq.register_builtin_impl(
"__eq__",
fn1_met(NoneType, NoneType, Bool),
Const,
Public,
);
nonetype.register_trait(
NoneType,
builtin_poly("Eq", vec![ty_tp(NoneType)]),
nonetype_eq,
);
let mut nonetype_show = Self::builtin_methods("Show", 1);
nonetype_show.register_builtin_impl("to_str", fn0_met(NoneType, Str), Immutable, Public);
nonetype.register_trait(NoneType, builtin_mono("Show"), nonetype_show);
/* Type */ /* Type */
let mut type_ = Self::builtin_mono_class("Type", 2); let mut type_ = Self::builtin_mono_class("Type", 2);
type_.register_superclass(Obj, &obj); type_.register_superclass(Obj, &obj);
@ -1462,7 +1481,7 @@ impl Context {
); );
/* Str_mut */ /* Str_mut */
let mut str_mut = Self::builtin_mono_class("Str!", 2); let mut str_mut = Self::builtin_mono_class("Str!", 2);
str_mut.register_superclass(Str, &str_); str_mut.register_superclass(Str, &nonetype);
let mut str_mut_mutable = Self::builtin_methods("Mutable", 2); let mut str_mut_mutable = Self::builtin_methods("Mutable", 2);
str_mut_mutable.register_builtin_const("ImmutType", ValueObj::builtin_t(Str)); str_mut_mutable.register_builtin_const("ImmutType", ValueObj::builtin_t(Str));
let f_t = kw("f", func(vec![kw("old", Str)], None, vec![], Str)); let f_t = kw("f", func(vec![kw("old", Str)], None, vec![], Str));
@ -1653,6 +1672,7 @@ impl Context {
self.register_builtin_type(Ratio, ratio, Const); self.register_builtin_type(Ratio, ratio, Const);
self.register_builtin_type(Bool, bool_, Const); self.register_builtin_type(Bool, bool_, Const);
self.register_builtin_type(Str, str_, Const); self.register_builtin_type(Str, str_, Const);
self.register_builtin_type(NoneType, nonetype, Const);
self.register_builtin_type(Type, type_, Const); self.register_builtin_type(Type, type_, Const);
self.register_builtin_type(ClassType, class_type, Const); self.register_builtin_type(ClassType, class_type, Const);
self.register_builtin_type(g_module_t, generic_module, Const); self.register_builtin_type(g_module_t, generic_module, Const);
@ -2051,7 +2071,7 @@ impl Context {
self.register_builtin_decl("__rorng__", op_t.clone(), Private); self.register_builtin_decl("__rorng__", op_t.clone(), Private);
self.register_builtin_decl("__orng__", op_t, Private); self.register_builtin_decl("__orng__", op_t, Private);
// TODO: use existential type: |T: Type| (T, In(T)) -> Bool // TODO: use existential type: |T: Type| (T, In(T)) -> Bool
let op_t = bin_op(mono_q("T"), mono_q("I"), Bool); let op_t = bin_op(mono_q("I"), mono_q("T"), Bool);
let op_t = quant( let op_t = quant(
op_t, op_t,
set! { static_instance("T", Type), subtypeof(mono_q("I"), builtin_poly("In", vec![ty_tp(mono_q("T"))])) }, set! { static_instance("T", Type), subtypeof(mono_q("I"), builtin_poly("In", vec![ty_tp(mono_q("T"))])) },

View file

@ -87,6 +87,31 @@ impl Context {
}) })
} }
pub(crate) fn get_mut_current_scope_var(&mut self, name: &str) -> Option<&mut VarInfo> {
self.locals
.get_mut(name)
.or_else(|| self.decls.get_mut(name))
.or_else(|| {
self.params
.iter_mut()
.find(|(opt_name, _)| {
opt_name
.as_ref()
.map(|n| &n.inspect()[..] == name)
.unwrap_or(false)
})
.map(|(_, vi)| vi)
})
.or_else(|| {
for (_, methods) in self.methods_list.iter_mut() {
if let Some(vi) = methods.get_mut_current_scope_var(name) {
return Some(vi);
}
}
None
})
}
pub(crate) fn get_local_kv(&self, name: &str) -> Option<(&VarName, &VarInfo)> { pub(crate) fn get_local_kv(&self, name: &str) -> Option<(&VarName, &VarInfo)> {
self.locals.get_key_value(name) self.locals.get_key_value(name)
} }
@ -1161,7 +1186,7 @@ impl Context {
pub(crate) fn get_similar_name(&self, name: &str) -> Option<&str> { pub(crate) fn get_similar_name(&self, name: &str) -> Option<&str> {
let name = readable_name(name); let name = readable_name(name);
// TODO: add decls // REVIEW: add decls?
get_similar_name( get_similar_name(
self.params self.params
.iter() .iter()
@ -1298,35 +1323,6 @@ impl Context {
concatenated concatenated
} }
pub(crate) fn _get_nominal_super_trait_ctxs<'a>(
&'a self,
t: &Type,
) -> Option<impl Iterator<Item = &'a Context>> {
let ctx = self.get_nominal_type_ctx(t)?;
Some(ctx.super_traits.iter().map(|sup| {
let sup_ctx = self
.get_nominal_type_ctx(sup)
.unwrap_or_else(|| todo!("{} not found", sup));
sup_ctx
}))
}
pub(crate) fn _get_nominal_super_class_ctxs<'a>(
&'a self,
t: &Type,
) -> Option<impl Iterator<Item = &'a Context>> {
// if `t` is {S: Str | ...}, `ctx_t` will be Str
// else if `t` is Array(Int, 10), `ctx_t` will be Array(T, N) (if Array(Int, 10) is not specialized)
let ctx = self.get_nominal_type_ctx(t)?;
// t: {S: Str | ...} => ctx.super_traits: [Eq(Str), Mul(Nat), ...]
// => return: [(Str, Eq(Str)), (Str, Mul(Nat)), ...] (the content of &'a Type isn't {S: Str | ...})
Some(
ctx.super_classes
.iter()
.map(|sup| self.get_nominal_type_ctx(sup).unwrap()),
)
}
pub(crate) fn get_nominal_super_type_ctxs<'a>(&'a self, t: &Type) -> Option<Vec<&'a Context>> { pub(crate) fn get_nominal_super_type_ctxs<'a>(&'a self, t: &Type) -> Option<Vec<&'a Context>> {
match t { match t {
Type::FreeVar(fv) if fv.is_linked() => self.get_nominal_super_type_ctxs(&fv.crack()), Type::FreeVar(fv) if fv.is_linked() => self.get_nominal_super_type_ctxs(&fv.crack()),
@ -1364,6 +1360,7 @@ impl Context {
} }
} }
/// include `t` itself
fn get_simple_nominal_super_type_ctxs<'a>( fn get_simple_nominal_super_type_ctxs<'a>(
&'a self, &'a self,
t: &Type, t: &Type,
@ -1377,6 +1374,19 @@ impl Context {
Some(vec![ctx].into_iter().chain(sups)) Some(vec![ctx].into_iter().chain(sups))
} }
/// if `typ` is a refinement type, include the base type (refine.t)
pub(crate) fn get_super_classes(&self, typ: &Type) -> Option<impl Iterator<Item = Type>> {
self.get_nominal_type_ctx(typ).map(|ctx| {
let super_classes = ctx.super_classes.clone();
let derefined = typ.derefine();
if typ != &derefined {
vec![derefined].into_iter().chain(super_classes)
} else {
vec![].into_iter().chain(super_classes)
}
})
}
// TODO: Never // TODO: Never
pub(crate) fn get_nominal_type_ctx<'a>(&'a self, typ: &Type) -> Option<&'a Context> { pub(crate) fn get_nominal_type_ctx<'a>(&'a self, typ: &Type) -> Option<&'a Context> {
match typ { match typ {

View file

@ -281,6 +281,7 @@ pub enum OperationKind {
Import, Import,
PyImport, PyImport,
Del, Del,
AssertCast,
} }
impl OperationKind { impl OperationKind {

View file

@ -14,8 +14,9 @@ use ast::{DefId, Identifier, VarName};
use erg_parser::ast; use erg_parser::ast;
use erg_type::constructors::{func, func1, proc, ref_, ref_mut, v_enum}; use erg_type::constructors::{func, func1, proc, ref_, ref_mut, v_enum};
use erg_type::free::{Constraint, Cyclicity, FreeKind};
use erg_type::value::{GenTypeObj, TypeKind, TypeObj, ValueObj}; use erg_type::value::{GenTypeObj, TypeKind, TypeObj, ValueObj};
use erg_type::{ParamTy, SubrType, Type}; use erg_type::{HasType, ParamTy, SubrType, Type};
use crate::build_hir::HIRBuilder; use crate::build_hir::HIRBuilder;
use crate::context::{ use crate::context::{
@ -1076,4 +1077,64 @@ impl Context {
))) )))
} }
} }
pub(crate) fn cast(
&mut self,
type_spec: ast::TypeSpec,
call: &mut hir::Call,
) -> TyCheckResult<()> {
let target_t =
self.instantiate_typespec(&type_spec, None, None, RegistrationMode::Normal)?;
let lhs = enum_unwrap!(
call.args.get_mut_left_or_key("pred").unwrap(),
hir::Expr::BinOp
)
.lhs
.as_mut();
match (
self.supertype_of(lhs.ref_t(), &target_t),
self.subtype_of(lhs.ref_t(), &target_t),
) {
// assert 1 in {1}
(true, true) => {}
// assert x in Int (x: Nat)
(false, true) => {} // TODO: warn (needless)
// assert x in Nat (x: Int)
(true, false) => {
if let hir::Expr::Accessor(ref acc) = lhs {
self.change_var_type(acc, target_t.clone())?;
}
match lhs.ref_t() {
Type::FreeVar(fv) if fv.is_linked() => {
let constraint = Constraint::new_subtype_of(target_t, Cyclicity::Not);
fv.replace(FreeKind::new_unbound(self.level, constraint));
}
Type::FreeVar(fv) => {
let new_constraint = Constraint::new_subtype_of(target_t, Cyclicity::Not);
fv.update_constraint(new_constraint);
}
_ => {
*lhs.ref_mut_t() = target_t;
}
}
}
// assert x in Str (x: Int)
(false, false) => todo!(),
}
Ok(())
}
fn change_var_type(&mut self, acc: &hir::Accessor, t: Type) -> TyCheckResult<()> {
match acc {
hir::Accessor::Ident(ident) => {
if let Some(vi) = self.get_mut_current_scope_var(ident.inspect()) {
vi.t = t;
} else {
todo!()
}
}
_ => todo!("type casting of {acc}"),
}
Ok(())
}
} }

View file

@ -282,6 +282,20 @@ impl Args {
} }
} }
pub fn get_mut_left_or_key(&mut self, key: &str) -> Option<&mut Expr> {
if !self.pos_args.is_empty() {
Some(&mut self.pos_args.get_mut(0)?.expr)
} else if let Some(pos) = self
.kw_args
.iter()
.position(|arg| &arg.keyword.inspect()[..] == key)
{
Some(&mut self.kw_args.get_mut(pos)?.expr)
} else {
None
}
}
pub fn insert_pos(&mut self, idx: usize, pos: PosArg) { pub fn insert_pos(&mut self, idx: usize, pos: PosArg) {
self.pos_args.insert(idx, pos); self.pos_args.insert(idx, pos);
} }

View file

@ -15,6 +15,7 @@ use erg_parser::ast;
use erg_parser::ast::AST; use erg_parser::ast::AST;
use erg_parser::build_ast::ASTBuilder; use erg_parser::build_ast::ASTBuilder;
use erg_parser::token::{Token, TokenKind}; use erg_parser::token::{Token, TokenKind};
use erg_parser::Parser;
use erg_type::constructors::{ use erg_type::constructors::{
array, array_mut, builtin_mono, builtin_poly, free_var, func, mono, proc, quant, set, set_mut, array, array_mut, builtin_mono, builtin_poly, free_var, func, mono, proc, quant, set, set_mut,
@ -549,9 +550,27 @@ impl ASTLowerer {
Ok(hir::UnaryOp::new(unary.op, expr, t)) Ok(hir::UnaryOp::new(unary.op, expr, t))
} }
// TODO: single `import`
fn lower_call(&mut self, call: ast::Call) -> LowerResult<hir::Call> { fn lower_call(&mut self, call: ast::Call) -> LowerResult<hir::Call> {
log!(info "entered {}({}{}(...))", fn_name!(), call.obj, fmt_option!(call.method_name)); log!(info "entered {}({}{}(...))", fn_name!(), call.obj, fmt_option!(call.method_name));
let assert_cast_target_type = if call.is_assert_cast() {
if let Some(typ) = call.assert_cast_target_type() {
Some(Parser::expr_to_type_spec(typ.clone()).map_err(|e| {
let e = LowerError::new(e.into(), self.input().clone(), self.ctx.caused_by());
LowerErrors::from(e)
})?)
} else {
return Err(LowerErrors::from(LowerError::syntax_error(
self.input().clone(),
line!() as usize,
call.args.loc(),
self.ctx.caused_by(),
"invalid assert casting type",
None,
)));
}
} else {
None
};
let (pos_args, kw_args, paren) = call.args.deconstruct(); let (pos_args, kw_args, paren) = call.args.deconstruct();
let mut hir_args = hir::Args::new( let mut hir_args = hir::Args::new(
Vec::with_capacity(pos_args.len()), Vec::with_capacity(pos_args.len()),
@ -584,7 +603,7 @@ impl ASTLowerer {
} else { } else {
None None
}; };
let call = hir::Call::new(obj, method_name, hir_args, sig_t); let mut call = hir::Call::new(obj, method_name, hir_args, sig_t);
match call.additional_operation() { match call.additional_operation() {
Some(kind @ (OperationKind::Import | OperationKind::PyImport)) => { Some(kind @ (OperationKind::Import | OperationKind::PyImport)) => {
let mod_name = let mod_name =
@ -608,7 +627,12 @@ impl ASTLowerer {
))) )))
} }
}, },
_ => {} _ => {
if let Some(type_spec) = assert_cast_target_type {
log!(err "cast({type_spec}): {call}");
self.ctx.cast(type_spec, &mut call)?;
}
}
} }
Ok(call) Ok(call)
} }

View file

@ -11,7 +11,7 @@ use erg_common::{
fmt_option, fmt_vec, impl_display_for_enum, impl_display_for_single_struct, fmt_option, fmt_vec, impl_display_for_enum, impl_display_for_single_struct,
impl_display_from_nested, impl_displayable_stream_for_wrapper, impl_locational, impl_display_from_nested, impl_displayable_stream_for_wrapper, impl_locational,
impl_locational_for_enum, impl_nested_display_for_chunk_enum, impl_nested_display_for_enum, impl_locational_for_enum, impl_nested_display_for_chunk_enum, impl_nested_display_for_enum,
impl_stream, impl_stream_for_wrapper, impl_stream, impl_stream_for_wrapper, option_enum_unwrap,
}; };
use erg_common::{fmt_vec_split_with, Str}; use erg_common::{fmt_vec_split_with, Str};
@ -939,6 +939,32 @@ impl Call {
args, args,
} }
} }
pub fn is_match(&self) -> bool {
self.obj
.get_name()
.map(|s| &s[..] == "match")
.unwrap_or(false)
}
pub fn is_assert_cast(&self) -> bool {
self.obj
.get_name()
.map(|s| &s[..] == "assert")
.unwrap_or(false)
&& self
.args
.get_left_or_key("pred")
.map(|pred| pred.is_bin_in())
.unwrap_or(false)
}
pub fn assert_cast_target_type(&self) -> Option<&Expr> {
self.args
.get_left_or_key("pred")
.and_then(|pred| option_enum_unwrap!(pred, Expr::BinOp))
.map(|bin| bin.args[1].as_ref())
}
} }
/// e.g. `Data::{x = 1; y = 2}` /// e.g. `Data::{x = 1; y = 2}`
@ -3111,7 +3137,11 @@ impl_locational_for_enum!(Expr; Lit, Accessor, Array, Tuple, Dict, Set, Record,
impl Expr { impl Expr {
pub fn is_match_call(&self) -> bool { pub fn is_match_call(&self) -> bool {
matches!(self, Expr::Call(Call{ obj, .. }) if obj.get_name().map(|s| &s[..] == "match").unwrap_or(false)) matches!(self, Expr::Call(call) if call.is_match())
}
pub fn is_bin_in(&self) -> bool {
matches!(self, Expr::BinOp(bin) if bin.op.is(TokenKind::InOp))
} }
pub fn is_const_acc(&self) -> bool { pub fn is_const_acc(&self) -> bool {

View file

@ -11,6 +11,18 @@ use erg_common::{impl_display_and_error, impl_stream_for_wrapper, switch_lang};
#[derive(Debug)] #[derive(Debug)]
pub struct LexError(ErrorCore); pub struct LexError(ErrorCore);
impl From<ErrorCore> for LexError {
fn from(core: ErrorCore) -> Self {
Self(core)
}
}
impl From<LexError> for ErrorCore {
fn from(err: LexError) -> Self {
err.0
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct LexErrors(Vec<LexError>); pub struct LexErrors(Vec<LexError>);

View file

@ -325,6 +325,17 @@ impl<T> FreeKind<T> {
} }
} }
pub fn new_unbound(lev: Level, constraint: Constraint) -> Self {
UNBOUND_ID.with(|id| {
*id.borrow_mut() += 1;
Self::Unbound {
id: *id.borrow(),
lev,
constraint,
}
})
}
pub const fn named_unbound(name: Str, lev: Level, constraint: Constraint) -> Self { pub const fn named_unbound(name: Str, lev: Level, constraint: Constraint) -> Self {
Self::NamedUnbound { Self::NamedUnbound {
name, name,
@ -440,6 +451,14 @@ impl<T: Clone + HasLevel> Free<T> {
*self.borrow_mut() = FreeKind::Linked(to.clone()); *self.borrow_mut() = FreeKind::Linked(to.clone());
} }
pub fn replace(&self, to: FreeKind<T>) {
// prevent linking to self
if self.is_linked() && addr_eq!(*self.borrow(), to) {
return;
}
*self.borrow_mut() = to;
}
/// NOTE: Do not use this except to rewrite circular references. /// NOTE: Do not use this except to rewrite circular references.
/// No reference to any type variable may be left behind when rewriting. /// No reference to any type variable may be left behind when rewriting.
/// However, `get_bound_types` is safe because it does not return references. /// However, `get_bound_types` is safe because it does not return references.

3
examples/assert_cast.er Normal file
View file

@ -0,0 +1,3 @@
i: Int = 2 - 1
assert i in Nat
i: Nat

View file

@ -11,6 +11,12 @@ fn exec_addition() -> Result<(), ()> {
expect_failure("tests/addition.er") expect_failure("tests/addition.er")
} }
#[test]
fn exec_assert_cast() -> Result<(), ()> {
// runtime `in` is not implemented
expect_end_with("examples/assert_cast.er", 1)
}
#[test] #[test]
fn exec_class() -> Result<(), ()> { fn exec_class() -> Result<(), ()> {
expect_success("examples/class.er") expect_success("examples/class.er")