mirror of
https://github.com/erg-lang/erg.git
synced 2025-08-04 18:58:30 +00:00
Add procedure assignment check
This commit is contained in:
parent
2f33c9b15d
commit
6d903d2575
7 changed files with 81 additions and 29 deletions
|
@ -633,10 +633,12 @@ impl Context {
|
|||
))
|
||||
} else {
|
||||
match obj {
|
||||
ValueObj::Type(t) => {
|
||||
let gen = enum_unwrap!(t, TypeObj::Generated);
|
||||
self.register_gen_type(ident, gen);
|
||||
}
|
||||
ValueObj::Type(t) => match t {
|
||||
TypeObj::Generated(gen) => {
|
||||
self.register_gen_type(ident, gen);
|
||||
}
|
||||
TypeObj::Builtin(_t) => panic!("aliasing bug"),
|
||||
},
|
||||
// TODO: not all value objects are comparable
|
||||
other => {
|
||||
let id = DefId(get_hash(ident));
|
||||
|
|
|
@ -9,8 +9,10 @@ use erg_common::vis::Visibility;
|
|||
use erg_common::Str;
|
||||
use Visibility::*;
|
||||
|
||||
use erg_type::HasType;
|
||||
|
||||
use crate::error::{EffectError, EffectErrors};
|
||||
use crate::hir::{Accessor, Array, Def, Expr, Signature, Tuple, HIR};
|
||||
use crate::hir::{Array, Def, Expr, Signature, Tuple, HIR};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
enum BlockKind {
|
||||
|
@ -168,6 +170,18 @@ impl SideEffectChecker {
|
|||
}
|
||||
|
||||
fn check_def(&mut self, def: &Def) {
|
||||
if !def.sig.is_subr() {
|
||||
let expr = def.body.block.last().unwrap();
|
||||
if !def.sig.is_procedural() && expr.t().is_procedural() {
|
||||
self.errs.push(EffectError::proc_assign_error(
|
||||
self.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
&def.sig,
|
||||
self.full_path(),
|
||||
));
|
||||
}
|
||||
return;
|
||||
}
|
||||
let name_and_vis = match &def.sig {
|
||||
Signature::Var(var) => (var.inspect().clone(), var.vis()),
|
||||
Signature::Subr(subr) => (subr.ident.inspect().clone(), subr.ident.vis()),
|
||||
|
@ -268,7 +282,7 @@ impl SideEffectChecker {
|
|||
}
|
||||
// 引数がproceduralでも関数呼び出しなら副作用なし
|
||||
Expr::Call(call) => {
|
||||
if (self.is_procedural(&call.obj)
|
||||
if (call.obj.t().is_procedural()
|
||||
|| call
|
||||
.method_name
|
||||
.as_ref()
|
||||
|
@ -318,19 +332,4 @@ impl SideEffectChecker {
|
|||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn is_procedural(&self, expr: &Expr) -> bool {
|
||||
match expr {
|
||||
Expr::Lambda(lambda) => lambda.is_procedural(),
|
||||
// 引数がproceduralでも関数呼び出しなら副作用なし
|
||||
Expr::Call(call) => self.is_procedural(&call.obj),
|
||||
Expr::Accessor(Accessor::Ident(ident)) => ident.name.is_procedural(),
|
||||
// procedural: x.y! (e.g. Array.sample!)
|
||||
// !procedural: !x.y
|
||||
Expr::Accessor(Accessor::Attr(attr)) => attr.ident.is_procedural(),
|
||||
Expr::Accessor(_) => todo!(),
|
||||
Expr::TypeAsc(tasc) => self.is_procedural(&tasc.expr),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ use erg_parser::error::{ParserRunnerError, ParserRunnerErrors};
|
|||
|
||||
use erg_type::{Predicate, Type};
|
||||
|
||||
use crate::hir::{Expr, Identifier};
|
||||
use crate::hir::{Expr, Identifier, Signature};
|
||||
|
||||
/// dname is for "double under name"
|
||||
pub fn binop_to_dname(op: &str) -> &str {
|
||||
|
@ -992,6 +992,38 @@ impl EffectError {
|
|||
caused_by.into(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn proc_assign_error<S: Into<AtomicStr>>(
|
||||
input: Input,
|
||||
errno: usize,
|
||||
sig: &Signature,
|
||||
caused_by: S,
|
||||
) -> Self {
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
errno,
|
||||
HasEffect,
|
||||
sig.loc(),
|
||||
switch_lang!(
|
||||
"japanese" => "プロシージャを通常の変数に代入することはできません",
|
||||
"simplified_chinese" => "不能将过程赋值给普通变量",
|
||||
"traditional_chinese" => "不能將過程賦值給普通變量",
|
||||
"english" => "cannot assign a procedure to a normal variable",
|
||||
),
|
||||
Some(
|
||||
switch_lang!(
|
||||
"japanese" => "変数の末尾に`!`をつけてください",
|
||||
"simplified_chinese" => "请在变量名后加上`!`",
|
||||
"traditional_chinese" => "請在變量名後加上`!`",
|
||||
"english" => "add `!` to the end of the variable name",
|
||||
)
|
||||
.into(),
|
||||
),
|
||||
),
|
||||
input,
|
||||
caused_by.into(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub type OwnershipError = TyCheckError;
|
||||
|
|
|
@ -252,7 +252,7 @@ impl ASTLowerer {
|
|||
let maybe_len = self.ctx.eval_const_expr(len, None);
|
||||
match maybe_len {
|
||||
Ok(v @ ValueObj::Nat(_)) => {
|
||||
if elem.ref_t().is_mut() {
|
||||
if elem.ref_t().is_mut_type() {
|
||||
builtin_poly(
|
||||
"ArrayWithMutType!",
|
||||
vec![TyParam::t(elem.t()), TyParam::Value(v)],
|
||||
|
@ -262,7 +262,7 @@ impl ASTLowerer {
|
|||
}
|
||||
}
|
||||
Ok(v @ ValueObj::Mut(_)) if v.class() == builtin_mono("Nat!") => {
|
||||
if elem.ref_t().is_mut() {
|
||||
if elem.ref_t().is_mut_type() {
|
||||
builtin_poly(
|
||||
"ArrayWithMutTypeAndLength!",
|
||||
vec![TyParam::t(elem.t()), TyParam::Value(v)],
|
||||
|
@ -274,7 +274,7 @@ impl ASTLowerer {
|
|||
Ok(other) => todo!("{other} is not a Nat object"),
|
||||
// REVIEW: is it ok to ignore the error?
|
||||
Err(_e) => {
|
||||
if elem.ref_t().is_mut() {
|
||||
if elem.ref_t().is_mut_type() {
|
||||
builtin_poly(
|
||||
"ArrayWithMutType!",
|
||||
vec![TyParam::t(elem.t()), TyParam::erased(Type::Nat)],
|
||||
|
|
|
@ -204,7 +204,7 @@ impl OwnershipChecker {
|
|||
self.errs.push(e);
|
||||
return;
|
||||
}
|
||||
if acc.ref_t().is_mut() && ownership.is_owned() && !chunk {
|
||||
if acc.ref_t().is_mut_type() && ownership.is_owned() && !chunk {
|
||||
self.drop(ident);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1684,11 +1684,25 @@ impl Type {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn is_mut(&self) -> bool {
|
||||
/// Procedure or MutType?
|
||||
pub fn is_procedural(&self) -> bool {
|
||||
match self {
|
||||
Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_procedural(),
|
||||
Self::Callable { .. } => true,
|
||||
Self::Subr(subr) if subr.kind == SubrKind::Proc => true,
|
||||
Self::Refinement(refine) =>
|
||||
refine.t.is_procedural() || refine.preds.iter().any(|pred|
|
||||
matches!(pred, Predicate::Equal{ rhs, .. } if pred.mentions(&refine.var) && rhs.name().map(|n| n.ends_with('!')).unwrap_or(false))
|
||||
),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_mut_type(&self) -> bool {
|
||||
match self {
|
||||
Self::FreeVar(fv) => {
|
||||
if fv.is_linked() {
|
||||
fv.crack().is_mut()
|
||||
fv.crack().is_mut_type()
|
||||
} else {
|
||||
fv.unbound_name().unwrap().ends_with('!')
|
||||
}
|
||||
|
@ -1700,7 +1714,7 @@ impl Type {
|
|||
| Self::BuiltinPoly { name, .. }
|
||||
| Self::PolyQVar { name, .. }
|
||||
| Self::MonoProj { rhs: name, .. } => name.ends_with('!'),
|
||||
Self::Refinement(refine) => refine.t.is_mut(),
|
||||
Self::Refinement(refine) => refine.t.is_mut_type(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,3 +7,8 @@ if True, do:
|
|||
|
||||
f x: Int = log x
|
||||
g x: Int = print! x # this should cause an effect error
|
||||
|
||||
echo = print! # this should be an effect error
|
||||
_echo = # this is OK
|
||||
print! 1
|
||||
log
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue