mirror of
https://github.com/erg-lang/erg.git
synced 2025-10-03 05:54:33 +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 {
|
} else {
|
||||||
match obj {
|
match obj {
|
||||||
ValueObj::Type(t) => {
|
ValueObj::Type(t) => match t {
|
||||||
let gen = enum_unwrap!(t, TypeObj::Generated);
|
TypeObj::Generated(gen) => {
|
||||||
self.register_gen_type(ident, gen);
|
self.register_gen_type(ident, gen);
|
||||||
}
|
}
|
||||||
|
TypeObj::Builtin(_t) => panic!("aliasing bug"),
|
||||||
|
},
|
||||||
// TODO: not all value objects are comparable
|
// TODO: not all value objects are comparable
|
||||||
other => {
|
other => {
|
||||||
let id = DefId(get_hash(ident));
|
let id = DefId(get_hash(ident));
|
||||||
|
|
|
@ -9,8 +9,10 @@ use erg_common::vis::Visibility;
|
||||||
use erg_common::Str;
|
use erg_common::Str;
|
||||||
use Visibility::*;
|
use Visibility::*;
|
||||||
|
|
||||||
|
use erg_type::HasType;
|
||||||
|
|
||||||
use crate::error::{EffectError, EffectErrors};
|
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)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
enum BlockKind {
|
enum BlockKind {
|
||||||
|
@ -168,6 +170,18 @@ impl SideEffectChecker {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_def(&mut self, def: &Def) {
|
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 {
|
let name_and_vis = match &def.sig {
|
||||||
Signature::Var(var) => (var.inspect().clone(), var.vis()),
|
Signature::Var(var) => (var.inspect().clone(), var.vis()),
|
||||||
Signature::Subr(subr) => (subr.ident.inspect().clone(), subr.ident.vis()),
|
Signature::Subr(subr) => (subr.ident.inspect().clone(), subr.ident.vis()),
|
||||||
|
@ -268,7 +282,7 @@ impl SideEffectChecker {
|
||||||
}
|
}
|
||||||
// 引数がproceduralでも関数呼び出しなら副作用なし
|
// 引数がproceduralでも関数呼び出しなら副作用なし
|
||||||
Expr::Call(call) => {
|
Expr::Call(call) => {
|
||||||
if (self.is_procedural(&call.obj)
|
if (call.obj.t().is_procedural()
|
||||||
|| call
|
|| call
|
||||||
.method_name
|
.method_name
|
||||||
.as_ref()
|
.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 erg_type::{Predicate, Type};
|
||||||
|
|
||||||
use crate::hir::{Expr, Identifier};
|
use crate::hir::{Expr, Identifier, Signature};
|
||||||
|
|
||||||
/// dname is for "double under name"
|
/// dname is for "double under name"
|
||||||
pub fn binop_to_dname(op: &str) -> &str {
|
pub fn binop_to_dname(op: &str) -> &str {
|
||||||
|
@ -992,6 +992,38 @@ impl EffectError {
|
||||||
caused_by.into(),
|
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;
|
pub type OwnershipError = TyCheckError;
|
||||||
|
|
|
@ -252,7 +252,7 @@ impl ASTLowerer {
|
||||||
let maybe_len = self.ctx.eval_const_expr(len, None);
|
let maybe_len = self.ctx.eval_const_expr(len, None);
|
||||||
match maybe_len {
|
match maybe_len {
|
||||||
Ok(v @ ValueObj::Nat(_)) => {
|
Ok(v @ ValueObj::Nat(_)) => {
|
||||||
if elem.ref_t().is_mut() {
|
if elem.ref_t().is_mut_type() {
|
||||||
builtin_poly(
|
builtin_poly(
|
||||||
"ArrayWithMutType!",
|
"ArrayWithMutType!",
|
||||||
vec![TyParam::t(elem.t()), TyParam::Value(v)],
|
vec![TyParam::t(elem.t()), TyParam::Value(v)],
|
||||||
|
@ -262,7 +262,7 @@ impl ASTLowerer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(v @ ValueObj::Mut(_)) if v.class() == builtin_mono("Nat!") => {
|
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(
|
builtin_poly(
|
||||||
"ArrayWithMutTypeAndLength!",
|
"ArrayWithMutTypeAndLength!",
|
||||||
vec![TyParam::t(elem.t()), TyParam::Value(v)],
|
vec![TyParam::t(elem.t()), TyParam::Value(v)],
|
||||||
|
@ -274,7 +274,7 @@ impl ASTLowerer {
|
||||||
Ok(other) => todo!("{other} is not a Nat object"),
|
Ok(other) => todo!("{other} is not a Nat object"),
|
||||||
// REVIEW: is it ok to ignore the error?
|
// REVIEW: is it ok to ignore the error?
|
||||||
Err(_e) => {
|
Err(_e) => {
|
||||||
if elem.ref_t().is_mut() {
|
if elem.ref_t().is_mut_type() {
|
||||||
builtin_poly(
|
builtin_poly(
|
||||||
"ArrayWithMutType!",
|
"ArrayWithMutType!",
|
||||||
vec![TyParam::t(elem.t()), TyParam::erased(Type::Nat)],
|
vec![TyParam::t(elem.t()), TyParam::erased(Type::Nat)],
|
||||||
|
|
|
@ -204,7 +204,7 @@ impl OwnershipChecker {
|
||||||
self.errs.push(e);
|
self.errs.push(e);
|
||||||
return;
|
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);
|
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 {
|
match self {
|
||||||
Self::FreeVar(fv) => {
|
Self::FreeVar(fv) => {
|
||||||
if fv.is_linked() {
|
if fv.is_linked() {
|
||||||
fv.crack().is_mut()
|
fv.crack().is_mut_type()
|
||||||
} else {
|
} else {
|
||||||
fv.unbound_name().unwrap().ends_with('!')
|
fv.unbound_name().unwrap().ends_with('!')
|
||||||
}
|
}
|
||||||
|
@ -1700,7 +1714,7 @@ impl Type {
|
||||||
| Self::BuiltinPoly { name, .. }
|
| Self::BuiltinPoly { name, .. }
|
||||||
| Self::PolyQVar { name, .. }
|
| Self::PolyQVar { name, .. }
|
||||||
| Self::MonoProj { rhs: name, .. } => name.ends_with('!'),
|
| Self::MonoProj { rhs: name, .. } => name.ends_with('!'),
|
||||||
Self::Refinement(refine) => refine.t.is_mut(),
|
Self::Refinement(refine) => refine.t.is_mut_type(),
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,3 +7,8 @@ if True, do:
|
||||||
|
|
||||||
f x: Int = log x
|
f x: Int = log x
|
||||||
g x: Int = print! x # this should cause an effect error
|
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