mirror of
https://github.com/erg-lang/erg.git
synced 2025-09-30 04:44:44 +00:00
Fix #247
This commit is contained in:
parent
1a9ae3349d
commit
111a9f5615
23 changed files with 213 additions and 97 deletions
|
@ -57,8 +57,8 @@ impl Context {
|
|||
found.union_types()
|
||||
.map(|(t1, t2)| format!("cannot {verb} {t1} {preposition} {t2}"))
|
||||
.or_else(|| {
|
||||
let ts = expected.inner_ts();
|
||||
Some(format!("cannot {verb} {found} {preposition} {}", ts[0]))
|
||||
let expected_inner = Self::readable_type(&expected.inner_ts()[0]);
|
||||
Some(format!("cannot {verb} {found} {preposition} {expected_inner}"))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -849,7 +849,7 @@ impl Context {
|
|||
/// substitute_call(instance: ((?T, Int) -> ?T), [Int, Nat], []) => instance: (Int, Int) -> Str
|
||||
/// substitute_call(instance: ((?M(: Nat)..?N(: Nat)) -> ?M+?N), [1..2], []) => instance: (1..2) -> {3}
|
||||
/// substitute_call(instance: ((?L(: Add(?R, ?O)), ?R) -> ?O), [1, 2], []) => instance: (Nat, Nat) -> Nat
|
||||
/// substitute_call(instance: ?T, [Int, Str], []) => instance: (Int, Str) -> Int
|
||||
/// substitute_call(instance: ((Failure, ?T) -> ?T), [Int, Int]) => instance: (Failure, Int) -> Int
|
||||
/// ```
|
||||
fn substitute_call(
|
||||
&self,
|
||||
|
|
|
@ -205,31 +205,47 @@ impl Context {
|
|||
sig: &ast::SubrSignature,
|
||||
default_ts: Vec<Type>,
|
||||
mode: RegistrationMode,
|
||||
) -> TyCheckResult<Type> {
|
||||
) -> Result<Type, (TyCheckErrors, Type)> {
|
||||
let mut errs = TyCheckErrors::empty();
|
||||
// -> Result<Type, (Type, TyCheckErrors)> {
|
||||
let opt_decl_sig_t = self
|
||||
.rec_get_decl_info(&sig.ident, AccessKind::Name, &self.cfg.input, &self.name)
|
||||
.ok()
|
||||
.map(|vi| enum_unwrap!(vi.t, Type::Subr));
|
||||
let mut tmp_tv_cache = self.instantiate_ty_bounds(&sig.bounds, PreRegister)?;
|
||||
let mut tmp_tv_cache = self
|
||||
.instantiate_ty_bounds(&sig.bounds, PreRegister)
|
||||
.map_err(|errs| (errs, Type::Failure))?;
|
||||
let mut non_defaults = vec![];
|
||||
for (n, p) in sig.params.non_defaults.iter().enumerate() {
|
||||
for (n, param) in sig.params.non_defaults.iter().enumerate() {
|
||||
let opt_decl_t = opt_decl_sig_t
|
||||
.as_ref()
|
||||
.and_then(|subr| subr.non_default_params.get(n));
|
||||
non_defaults.push(self.instantiate_param_ty(
|
||||
p,
|
||||
None,
|
||||
opt_decl_t,
|
||||
&mut tmp_tv_cache,
|
||||
mode,
|
||||
)?);
|
||||
match self.instantiate_param_ty(param, None, opt_decl_t, &mut tmp_tv_cache, mode) {
|
||||
Ok(t) => non_defaults.push(t),
|
||||
Err(es) => {
|
||||
errs.extend(es);
|
||||
non_defaults.push(ParamTy::pos(param.inspect().cloned(), Type::Failure));
|
||||
}
|
||||
}
|
||||
}
|
||||
let var_args = if let Some(var_args) = sig.params.var_args.as_ref() {
|
||||
let opt_decl_t = opt_decl_sig_t
|
||||
.as_ref()
|
||||
.and_then(|subr| subr.var_params.as_ref().map(|v| v.as_ref()));
|
||||
Some(self.instantiate_param_ty(var_args, None, opt_decl_t, &mut tmp_tv_cache, mode)?)
|
||||
let pt = match self.instantiate_param_ty(
|
||||
var_args,
|
||||
None,
|
||||
opt_decl_t,
|
||||
&mut tmp_tv_cache,
|
||||
mode,
|
||||
) {
|
||||
Ok(pt) => pt,
|
||||
Err(es) => {
|
||||
errs.extend(es);
|
||||
ParamTy::pos(var_args.inspect().cloned(), Type::Failure)
|
||||
}
|
||||
};
|
||||
Some(pt)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
@ -238,19 +254,32 @@ impl Context {
|
|||
let opt_decl_t = opt_decl_sig_t
|
||||
.as_ref()
|
||||
.and_then(|subr| subr.default_params.get(n));
|
||||
defaults.push(self.instantiate_param_ty(
|
||||
match self.instantiate_param_ty(
|
||||
&p.sig,
|
||||
Some(default_t),
|
||||
opt_decl_t,
|
||||
&mut tmp_tv_cache,
|
||||
mode,
|
||||
)?);
|
||||
) {
|
||||
Ok(t) => defaults.push(t),
|
||||
Err(es) => {
|
||||
errs.extend(es);
|
||||
defaults.push(ParamTy::pos(p.sig.inspect().cloned(), Type::Failure));
|
||||
}
|
||||
}
|
||||
}
|
||||
let spec_return_t = if let Some(s) = sig.return_t_spec.as_ref() {
|
||||
let opt_decl_t = opt_decl_sig_t
|
||||
.as_ref()
|
||||
.map(|subr| ParamTy::anonymous(subr.return_t.as_ref().clone()));
|
||||
self.instantiate_typespec(s, opt_decl_t.as_ref(), &mut tmp_tv_cache, mode, false)?
|
||||
match self.instantiate_typespec(s, opt_decl_t.as_ref(), &mut tmp_tv_cache, mode, false)
|
||||
{
|
||||
Ok(ty) => ty,
|
||||
Err(es) => {
|
||||
errs.extend(es);
|
||||
Type::Failure
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// preregisterならouter scopeで型宣言(see inference.md)
|
||||
let level = if mode == PreRegister {
|
||||
|
@ -260,11 +289,16 @@ impl Context {
|
|||
};
|
||||
free_var(level, Constraint::new_type_of(Type))
|
||||
};
|
||||
Ok(if sig.ident.is_procedural() {
|
||||
let typ = if sig.ident.is_procedural() {
|
||||
proc(non_defaults, var_args, defaults, spec_return_t)
|
||||
} else {
|
||||
func(non_defaults, var_args, defaults, spec_return_t)
|
||||
})
|
||||
};
|
||||
if errs.is_empty() {
|
||||
Ok(typ)
|
||||
} else {
|
||||
Err((errs, typ))
|
||||
}
|
||||
}
|
||||
|
||||
/// spec_t == Noneかつリテラル推論が不可能なら型変数を発行する
|
||||
|
@ -454,12 +488,12 @@ impl Context {
|
|||
tmp_tv_cache.push_or_init_tyvar(&Str::rc(other), &tyvar);
|
||||
Ok(tyvar)
|
||||
} else {
|
||||
Err(TyCheckErrors::from(TyCheckError::no_var_error(
|
||||
Err(TyCheckErrors::from(TyCheckError::no_type_error(
|
||||
self.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
simple.loc(),
|
||||
self.caused_by(),
|
||||
&format!("(Type) {other}"),
|
||||
other,
|
||||
self.get_similar_name(other),
|
||||
)))
|
||||
}
|
||||
|
@ -468,12 +502,12 @@ impl Context {
|
|||
let ctx = if let Some((_, ctx)) = self.rec_get_type(other) {
|
||||
ctx
|
||||
} else {
|
||||
return Err(TyCheckErrors::from(TyCheckError::no_var_error(
|
||||
return Err(TyCheckErrors::from(TyCheckError::no_type_error(
|
||||
self.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
simple.ident.loc(),
|
||||
self.caused_by(),
|
||||
&format!("(Type) {other}"),
|
||||
other,
|
||||
self.get_similar_name(other),
|
||||
)));
|
||||
};
|
||||
|
|
|
@ -115,21 +115,10 @@ impl Context {
|
|||
.collect::<Set<_>>();
|
||||
let default_ts =
|
||||
vec![free_var(self.level, Constraint::new_type_of(Type::Type)); sig.params.len()];
|
||||
let t = self
|
||||
.instantiate_sub_sig_t(sig, default_ts, PreRegister)
|
||||
.map_err(|e| {
|
||||
let vi = VarInfo::new(
|
||||
Type::Failure,
|
||||
muty,
|
||||
vis,
|
||||
kind.clone(),
|
||||
Some(comptime_decos.clone()),
|
||||
self.impl_of(),
|
||||
None,
|
||||
);
|
||||
self.decls.insert(sig.ident.name.clone(), vi);
|
||||
e
|
||||
})?;
|
||||
let (errs, t) = match self.instantiate_sub_sig_t(sig, default_ts, PreRegister) {
|
||||
Ok(t) => (TyCheckErrors::empty(), t),
|
||||
Err((errs, t)) => (errs, t),
|
||||
};
|
||||
let vi = VarInfo::new(
|
||||
t,
|
||||
muty,
|
||||
|
@ -149,7 +138,11 @@ impl Context {
|
|||
)))
|
||||
} else {
|
||||
self.decls.insert(sig.ident.name.clone(), vi);
|
||||
if errs.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(errs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -345,6 +338,7 @@ impl Context {
|
|||
params: &hir::Params,
|
||||
opt_decl_subr_t: Option<SubrType>,
|
||||
) -> TyCheckResult<()> {
|
||||
let mut errs = TyCheckErrors::empty();
|
||||
if let Some(decl_subr_t) = opt_decl_subr_t {
|
||||
assert_eq!(
|
||||
params.non_defaults.len(),
|
||||
|
@ -356,24 +350,36 @@ impl Context {
|
|||
.iter()
|
||||
.zip(decl_subr_t.non_default_params.iter())
|
||||
{
|
||||
self.assign_param(sig, false, Some(pt))?;
|
||||
if let Err(es) = self.assign_param(sig, false, Some(pt)) {
|
||||
errs.extend(es);
|
||||
}
|
||||
}
|
||||
for (sig, pt) in params
|
||||
.defaults
|
||||
.iter()
|
||||
.zip(decl_subr_t.default_params.iter())
|
||||
{
|
||||
self.assign_param(&sig.sig, true, Some(pt))?;
|
||||
if let Err(es) = self.assign_param(&sig.sig, true, Some(pt)) {
|
||||
errs.extend(es);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for sig in params.non_defaults.iter() {
|
||||
self.assign_param(sig, false, None)?;
|
||||
if let Err(es) = self.assign_param(sig, false, None) {
|
||||
errs.extend(es);
|
||||
}
|
||||
}
|
||||
for sig in params.defaults.iter() {
|
||||
self.assign_param(&sig.sig, true, None)?;
|
||||
if let Err(es) = self.assign_param(&sig.sig, true, None) {
|
||||
errs.extend(es);
|
||||
}
|
||||
}
|
||||
}
|
||||
if errs.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(errs)
|
||||
}
|
||||
}
|
||||
|
||||
/// ## Errors
|
||||
|
|
|
@ -1268,6 +1268,10 @@ impl Context {
|
|||
if maybe_sub == &Type::Never || maybe_sup == &Type::Obj || maybe_sup == maybe_sub {
|
||||
return Ok(());
|
||||
}
|
||||
// API definition was failed and inspection is useless after this
|
||||
if maybe_sub == &Type::Failure || maybe_sup == &Type::Failure {
|
||||
return Ok(());
|
||||
}
|
||||
self.occur(maybe_sub, maybe_sup, loc)?;
|
||||
let maybe_sub_is_sub = self.subtype_of(maybe_sub, maybe_sup);
|
||||
if maybe_sub.has_no_unbound_var() && maybe_sup.has_no_unbound_var() && maybe_sub_is_sub {
|
||||
|
|
|
@ -1577,6 +1577,43 @@ impl LowerError {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn no_type_error(
|
||||
input: Input,
|
||||
errno: usize,
|
||||
loc: Location,
|
||||
caused_by: String,
|
||||
name: &str,
|
||||
similar_name: Option<&str>,
|
||||
) -> Self {
|
||||
let name = readable_name(name);
|
||||
let hint = similar_name.map(|n| {
|
||||
let n = StyledStr::new(n, Some(HINT), Some(ATTR));
|
||||
switch_lang!(
|
||||
"japanese" => format!("似た名前の型があります: {n}"),
|
||||
"simplified_chinese" => format!("存在相同名称类型: {n}"),
|
||||
"traditional_chinese" => format!("存在相同名稱類型: {n}"),
|
||||
"english" => format!("exists a similar name type: {n}"),
|
||||
)
|
||||
});
|
||||
let found = StyledString::new(name, Some(ERR), Some(ATTR));
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
vec![SubMessage::ambiguous_new(loc, vec![], hint)],
|
||||
switch_lang!(
|
||||
"japanese" => format!("{found}という型は定義されていません"),
|
||||
"simplified_chinese" => format!("{found}未定义"),
|
||||
"traditional_chinese" => format!("{found}未定義"),
|
||||
"english" => format!("Type {found} is not defined"),
|
||||
),
|
||||
errno,
|
||||
NameError,
|
||||
loc,
|
||||
),
|
||||
input,
|
||||
caused_by,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn type_not_found(
|
||||
input: Input,
|
||||
errno: usize,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from _erg_result import Error
|
||||
|
||||
class Nat(int):
|
||||
def try_new(i: int): # -> Result[Nat]
|
||||
def try_new(i): # -> Result[Nat]
|
||||
if i >= 0:
|
||||
return Nat(i)
|
||||
else:
|
||||
|
@ -10,3 +10,9 @@ class Nat(int):
|
|||
def times(self, f):
|
||||
for _ in range(self):
|
||||
f()
|
||||
|
||||
def saturating_sub(self, other):
|
||||
if self > other:
|
||||
return self - other
|
||||
else:
|
||||
return 0
|
||||
|
|
|
@ -835,11 +835,19 @@ impl ASTLowerer {
|
|||
|
||||
fn lower_params(&mut self, params: ast::Params) -> LowerResult<hir::Params> {
|
||||
log!(info "entered {}({})", fn_name!(), params);
|
||||
let mut errs = LowerErrors::empty();
|
||||
let mut hir_defaults = vec![];
|
||||
for default in params.defaults.into_iter() {
|
||||
let default_val = self.lower_expr(default.default_val)?;
|
||||
match self.lower_expr(default.default_val) {
|
||||
Ok(default_val) => {
|
||||
hir_defaults.push(hir::DefaultParamSignature::new(default.sig, default_val));
|
||||
}
|
||||
Err(es) => errs.extend(es),
|
||||
}
|
||||
}
|
||||
if !errs.is_empty() {
|
||||
Err(errs)
|
||||
} else {
|
||||
let hir_params = hir::Params::new(
|
||||
params.non_defaults,
|
||||
params.var_args,
|
||||
|
@ -848,6 +856,7 @@ impl ASTLowerer {
|
|||
);
|
||||
Ok(hir_params)
|
||||
}
|
||||
}
|
||||
|
||||
/// TODO: varargs
|
||||
fn lower_lambda(&mut self, lambda: ast::Lambda) -> LowerResult<hir::Lambda> {
|
||||
|
|
4
tests/should_err/addition.er
Normal file
4
tests/should_err/addition.er
Normal file
|
@ -0,0 +1,4 @@
|
|||
add x, y =
|
||||
x + y
|
||||
|
||||
print! add("a", 1) # this will be an error
|
7
tests/should_err/invalid_param.er
Normal file
7
tests/should_err/invalid_param.er
Normal file
|
@ -0,0 +1,7 @@
|
|||
# Typo: Inu
|
||||
add x: Int, y: Inu, z: Int = x + y + z
|
||||
|
||||
# 3rd arg is wrong
|
||||
print! add(1, 2, "")
|
||||
# missing argument
|
||||
print! add(1, 2)
|
|
@ -4,4 +4,3 @@ add x, y =
|
|||
print! add(1, 1)
|
||||
print! add(1.0, 1)
|
||||
print! add("a", "b")
|
||||
print! add("a", 1) # this will be an error
|
|
@ -10,13 +10,13 @@ use erg_compiler::error::CompileErrors;
|
|||
use erg::DummyVM;
|
||||
|
||||
#[test]
|
||||
fn exec_addition() -> Result<(), ()> {
|
||||
expect_failure("tests/addition.er", 1)
|
||||
fn exec_addition_ok() -> Result<(), ()> {
|
||||
expect_success("tests/should_ok/addition.er")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_advanced_type_spec() -> Result<(), ()> {
|
||||
expect_success("tests/advanced_type_spec.er")
|
||||
expect_success("tests/should_ok/advanced_type_spec.er")
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -31,7 +31,7 @@ fn exec_class() -> Result<(), ()> {
|
|||
|
||||
#[test]
|
||||
fn exec_control() -> Result<(), ()> {
|
||||
expect_success("tests/control.er")
|
||||
expect_success("tests/should_ok/control.er")
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -66,31 +66,17 @@ fn exec_import() -> Result<(), ()> {
|
|||
|
||||
#[test]
|
||||
fn exec_infer_class() -> Result<(), ()> {
|
||||
expect_success("tests/infer_class.er")
|
||||
expect_success("tests/should_ok/infer_class.er")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_infer_trait() -> Result<(), ()> {
|
||||
expect_success("tests/infer_trait.er")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_move_check() -> Result<(), ()> {
|
||||
expect_failure("examples/move_check.er", 1)
|
||||
expect_success("tests/should_ok/infer_trait.er")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_pattern() -> Result<(), ()> {
|
||||
expect_success("tests/pattern.er")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_pyimport() -> Result<(), ()> {
|
||||
if cfg!(unix) {
|
||||
expect_end_with("examples/pyimport.er", 111)
|
||||
} else {
|
||||
expect_failure("examples/pyimport.er", 1)
|
||||
}
|
||||
expect_success("tests/should_ok/pattern.er")
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -103,32 +89,11 @@ fn exec_raw_ident() -> Result<(), ()> {
|
|||
expect_success("examples/raw_ident.er")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_rec() -> Result<(), ()> {
|
||||
// this script is valid but the current code generating process has a bug.
|
||||
expect_end_with("tests/rec.er", 1)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_record() -> Result<(), ()> {
|
||||
expect_success("examples/record.er")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_set() -> Result<(), ()> {
|
||||
expect_failure("examples/set.er", 1)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_side_effect() -> Result<(), ()> {
|
||||
expect_failure("examples/side_effect.er", 4)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_subtyping() -> Result<(), ()> {
|
||||
expect_failure("tests/subtyping.er", 1)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_trait() -> Result<(), ()> {
|
||||
expect_success("examples/trait.er")
|
||||
|
@ -154,6 +119,51 @@ fn exec_with() -> Result<(), ()> {
|
|||
expect_success("examples/with.er")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_addition_err() -> Result<(), ()> {
|
||||
expect_failure("tests/should_err/addition.er", 1)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_invalid_param() -> Result<(), ()> {
|
||||
expect_failure("tests/should_err/invalid_param.er", 3)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_move_check() -> Result<(), ()> {
|
||||
expect_failure("examples/move_check.er", 1)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_pyimport() -> Result<(), ()> {
|
||||
if cfg!(unix) {
|
||||
expect_end_with("examples/pyimport.er", 111)
|
||||
} else {
|
||||
expect_failure("examples/pyimport.er", 1)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_rec() -> Result<(), ()> {
|
||||
// this script is valid but the current code generating process has a bug.
|
||||
expect_end_with("tests/should_err/rec.er", 1)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_set() -> Result<(), ()> {
|
||||
expect_failure("examples/set.er", 1)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_side_effect() -> Result<(), ()> {
|
||||
expect_failure("examples/side_effect.er", 4)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn exec_subtyping() -> Result<(), ()> {
|
||||
expect_failure("tests/should_err/subtyping.er", 1)
|
||||
}
|
||||
|
||||
fn expect_success(file_path: &'static str) -> Result<(), ()> {
|
||||
match exec_vm(file_path) {
|
||||
Ok(0) => Ok(()),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue