mirror of
https://github.com/erg-lang/erg.git
synced 2025-09-30 21:01:10 +00:00
Implement assert casting
This commit is contained in:
parent
4e70e2e980
commit
de1180387c
13 changed files with 252 additions and 51 deletions
|
@ -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");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"))])) },
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -281,6 +281,7 @@ pub enum OperationKind {
|
||||||
Import,
|
Import,
|
||||||
PyImport,
|
PyImport,
|
||||||
Del,
|
Del,
|
||||||
|
AssertCast,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OperationKind {
|
impl OperationKind {
|
||||||
|
|
|
@ -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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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>);
|
||||||
|
|
||||||
|
|
|
@ -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
3
examples/assert_cast.er
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
i: Int = 2 - 1
|
||||||
|
assert i in Nat
|
||||||
|
i: Nat
|
|
@ -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")
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue