This commit is contained in:
Shunsuke Shibayama 2022-11-27 20:24:11 +09:00
parent 1a9ae3349d
commit 111a9f5615
23 changed files with 213 additions and 97 deletions

View file

@ -57,8 +57,8 @@ impl Context {
found.union_types() found.union_types()
.map(|(t1, t2)| format!("cannot {verb} {t1} {preposition} {t2}")) .map(|(t1, t2)| format!("cannot {verb} {t1} {preposition} {t2}"))
.or_else(|| { .or_else(|| {
let ts = expected.inner_ts(); let expected_inner = Self::readable_type(&expected.inner_ts()[0]);
Some(format!("cannot {verb} {found} {preposition} {}", ts[0])) Some(format!("cannot {verb} {found} {preposition} {expected_inner}"))
}) })
} }
} }

View file

@ -849,7 +849,7 @@ impl Context {
/// substitute_call(instance: ((?T, Int) -> ?T), [Int, Nat], []) => instance: (Int, Int) -> Str /// 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: ((?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: ((?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( fn substitute_call(
&self, &self,

View file

@ -205,31 +205,47 @@ impl Context {
sig: &ast::SubrSignature, sig: &ast::SubrSignature,
default_ts: Vec<Type>, default_ts: Vec<Type>,
mode: RegistrationMode, mode: RegistrationMode,
) -> TyCheckResult<Type> { ) -> Result<Type, (TyCheckErrors, Type)> {
let mut errs = TyCheckErrors::empty();
// -> Result<Type, (Type, TyCheckErrors)> { // -> Result<Type, (Type, TyCheckErrors)> {
let opt_decl_sig_t = self let opt_decl_sig_t = self
.rec_get_decl_info(&sig.ident, AccessKind::Name, &self.cfg.input, &self.name) .rec_get_decl_info(&sig.ident, AccessKind::Name, &self.cfg.input, &self.name)
.ok() .ok()
.map(|vi| enum_unwrap!(vi.t, Type::Subr)); .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![]; 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 let opt_decl_t = opt_decl_sig_t
.as_ref() .as_ref()
.and_then(|subr| subr.non_default_params.get(n)); .and_then(|subr| subr.non_default_params.get(n));
non_defaults.push(self.instantiate_param_ty( match self.instantiate_param_ty(param, None, opt_decl_t, &mut tmp_tv_cache, mode) {
p, Ok(t) => non_defaults.push(t),
None, Err(es) => {
opt_decl_t, errs.extend(es);
&mut tmp_tv_cache, non_defaults.push(ParamTy::pos(param.inspect().cloned(), Type::Failure));
mode, }
)?); }
} }
let var_args = if let Some(var_args) = sig.params.var_args.as_ref() { let var_args = if let Some(var_args) = sig.params.var_args.as_ref() {
let opt_decl_t = opt_decl_sig_t let opt_decl_t = opt_decl_sig_t
.as_ref() .as_ref()
.and_then(|subr| subr.var_params.as_ref().map(|v| v.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 { } else {
None None
}; };
@ -238,19 +254,32 @@ impl Context {
let opt_decl_t = opt_decl_sig_t let opt_decl_t = opt_decl_sig_t
.as_ref() .as_ref()
.and_then(|subr| subr.default_params.get(n)); .and_then(|subr| subr.default_params.get(n));
defaults.push(self.instantiate_param_ty( match self.instantiate_param_ty(
&p.sig, &p.sig,
Some(default_t), Some(default_t),
opt_decl_t, opt_decl_t,
&mut tmp_tv_cache, &mut tmp_tv_cache,
mode, 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 spec_return_t = if let Some(s) = sig.return_t_spec.as_ref() {
let opt_decl_t = opt_decl_sig_t let opt_decl_t = opt_decl_sig_t
.as_ref() .as_ref()
.map(|subr| ParamTy::anonymous(subr.return_t.as_ref().clone())); .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 { } else {
// preregisterならouter scopeで型宣言(see inference.md) // preregisterならouter scopeで型宣言(see inference.md)
let level = if mode == PreRegister { let level = if mode == PreRegister {
@ -260,11 +289,16 @@ impl Context {
}; };
free_var(level, Constraint::new_type_of(Type)) 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) proc(non_defaults, var_args, defaults, spec_return_t)
} else { } else {
func(non_defaults, var_args, defaults, spec_return_t) func(non_defaults, var_args, defaults, spec_return_t)
}) };
if errs.is_empty() {
Ok(typ)
} else {
Err((errs, typ))
}
} }
/// spec_t == Noneかつリテラル推論が不可能なら型変数を発行する /// spec_t == Noneかつリテラル推論が不可能なら型変数を発行する
@ -454,12 +488,12 @@ impl Context {
tmp_tv_cache.push_or_init_tyvar(&Str::rc(other), &tyvar); tmp_tv_cache.push_or_init_tyvar(&Str::rc(other), &tyvar);
Ok(tyvar) Ok(tyvar)
} else { } else {
Err(TyCheckErrors::from(TyCheckError::no_var_error( Err(TyCheckErrors::from(TyCheckError::no_type_error(
self.cfg.input.clone(), self.cfg.input.clone(),
line!() as usize, line!() as usize,
simple.loc(), simple.loc(),
self.caused_by(), self.caused_by(),
&format!("(Type) {other}"), other,
self.get_similar_name(other), self.get_similar_name(other),
))) )))
} }
@ -468,12 +502,12 @@ impl Context {
let ctx = if let Some((_, ctx)) = self.rec_get_type(other) { let ctx = if let Some((_, ctx)) = self.rec_get_type(other) {
ctx ctx
} else { } else {
return Err(TyCheckErrors::from(TyCheckError::no_var_error( return Err(TyCheckErrors::from(TyCheckError::no_type_error(
self.cfg.input.clone(), self.cfg.input.clone(),
line!() as usize, line!() as usize,
simple.ident.loc(), simple.ident.loc(),
self.caused_by(), self.caused_by(),
&format!("(Type) {other}"), other,
self.get_similar_name(other), self.get_similar_name(other),
))); )));
}; };

View file

@ -115,21 +115,10 @@ impl Context {
.collect::<Set<_>>(); .collect::<Set<_>>();
let default_ts = let default_ts =
vec![free_var(self.level, Constraint::new_type_of(Type::Type)); sig.params.len()]; vec![free_var(self.level, Constraint::new_type_of(Type::Type)); sig.params.len()];
let t = self let (errs, t) = match self.instantiate_sub_sig_t(sig, default_ts, PreRegister) {
.instantiate_sub_sig_t(sig, default_ts, PreRegister) Ok(t) => (TyCheckErrors::empty(), t),
.map_err(|e| { Err((errs, t)) => (errs, t),
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 vi = VarInfo::new( let vi = VarInfo::new(
t, t,
muty, muty,
@ -149,7 +138,11 @@ impl Context {
))) )))
} else { } else {
self.decls.insert(sig.ident.name.clone(), vi); self.decls.insert(sig.ident.name.clone(), vi);
Ok(()) if errs.is_empty() {
Ok(())
} else {
Err(errs)
}
} }
} }
@ -345,6 +338,7 @@ impl Context {
params: &hir::Params, params: &hir::Params,
opt_decl_subr_t: Option<SubrType>, opt_decl_subr_t: Option<SubrType>,
) -> TyCheckResult<()> { ) -> TyCheckResult<()> {
let mut errs = TyCheckErrors::empty();
if let Some(decl_subr_t) = opt_decl_subr_t { if let Some(decl_subr_t) = opt_decl_subr_t {
assert_eq!( assert_eq!(
params.non_defaults.len(), params.non_defaults.len(),
@ -356,24 +350,36 @@ impl Context {
.iter() .iter()
.zip(decl_subr_t.non_default_params.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 for (sig, pt) in params
.defaults .defaults
.iter() .iter()
.zip(decl_subr_t.default_params.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 { } else {
for sig in params.non_defaults.iter() { 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() { 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);
}
} }
} }
Ok(()) if errs.is_empty() {
Ok(())
} else {
Err(errs)
}
} }
/// ## Errors /// ## Errors

View file

@ -1268,6 +1268,10 @@ impl Context {
if maybe_sub == &Type::Never || maybe_sup == &Type::Obj || maybe_sup == maybe_sub { if maybe_sub == &Type::Never || maybe_sup == &Type::Obj || maybe_sup == maybe_sub {
return Ok(()); 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)?; self.occur(maybe_sub, maybe_sup, loc)?;
let maybe_sub_is_sub = self.subtype_of(maybe_sub, maybe_sup); 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 { if maybe_sub.has_no_unbound_var() && maybe_sup.has_no_unbound_var() && maybe_sub_is_sub {

View file

@ -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( pub fn type_not_found(
input: Input, input: Input,
errno: usize, errno: usize,

View file

@ -1,7 +1,7 @@
from _erg_result import Error from _erg_result import Error
class Nat(int): class Nat(int):
def try_new(i: int): # -> Result[Nat] def try_new(i): # -> Result[Nat]
if i >= 0: if i >= 0:
return Nat(i) return Nat(i)
else: else:
@ -10,3 +10,9 @@ class Nat(int):
def times(self, f): def times(self, f):
for _ in range(self): for _ in range(self):
f() f()
def saturating_sub(self, other):
if self > other:
return self - other
else:
return 0

View file

@ -835,18 +835,27 @@ impl ASTLowerer {
fn lower_params(&mut self, params: ast::Params) -> LowerResult<hir::Params> { fn lower_params(&mut self, params: ast::Params) -> LowerResult<hir::Params> {
log!(info "entered {}({})", fn_name!(), params); log!(info "entered {}({})", fn_name!(), params);
let mut errs = LowerErrors::empty();
let mut hir_defaults = vec![]; let mut hir_defaults = vec![];
for default in params.defaults.into_iter() { for default in params.defaults.into_iter() {
let default_val = self.lower_expr(default.default_val)?; match self.lower_expr(default.default_val) {
hir_defaults.push(hir::DefaultParamSignature::new(default.sig, 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,
hir_defaults,
params.parens,
);
Ok(hir_params)
} }
let hir_params = hir::Params::new(
params.non_defaults,
params.var_args,
hir_defaults,
params.parens,
);
Ok(hir_params)
} }
/// TODO: varargs /// TODO: varargs

View file

@ -0,0 +1,4 @@
add x, y =
x + y
print! add("a", 1) # this will be an error

View 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)

View file

@ -4,4 +4,3 @@ add x, y =
print! add(1, 1) print! add(1, 1)
print! add(1.0, 1) print! add(1.0, 1)
print! add("a", "b") print! add("a", "b")
print! add("a", 1) # this will be an error

View file

@ -10,13 +10,13 @@ use erg_compiler::error::CompileErrors;
use erg::DummyVM; use erg::DummyVM;
#[test] #[test]
fn exec_addition() -> Result<(), ()> { fn exec_addition_ok() -> Result<(), ()> {
expect_failure("tests/addition.er", 1) expect_success("tests/should_ok/addition.er")
} }
#[test] #[test]
fn exec_advanced_type_spec() -> Result<(), ()> { fn exec_advanced_type_spec() -> Result<(), ()> {
expect_success("tests/advanced_type_spec.er") expect_success("tests/should_ok/advanced_type_spec.er")
} }
#[test] #[test]
@ -31,7 +31,7 @@ fn exec_class() -> Result<(), ()> {
#[test] #[test]
fn exec_control() -> Result<(), ()> { fn exec_control() -> Result<(), ()> {
expect_success("tests/control.er") expect_success("tests/should_ok/control.er")
} }
#[test] #[test]
@ -66,31 +66,17 @@ fn exec_import() -> Result<(), ()> {
#[test] #[test]
fn exec_infer_class() -> Result<(), ()> { fn exec_infer_class() -> Result<(), ()> {
expect_success("tests/infer_class.er") expect_success("tests/should_ok/infer_class.er")
} }
#[test] #[test]
fn exec_infer_trait() -> Result<(), ()> { fn exec_infer_trait() -> Result<(), ()> {
expect_success("tests/infer_trait.er") expect_success("tests/should_ok/infer_trait.er")
}
#[test]
fn exec_move_check() -> Result<(), ()> {
expect_failure("examples/move_check.er", 1)
} }
#[test] #[test]
fn exec_pattern() -> Result<(), ()> { fn exec_pattern() -> Result<(), ()> {
expect_success("tests/pattern.er") expect_success("tests/should_ok/pattern.er")
}
#[test]
fn exec_pyimport() -> Result<(), ()> {
if cfg!(unix) {
expect_end_with("examples/pyimport.er", 111)
} else {
expect_failure("examples/pyimport.er", 1)
}
} }
#[test] #[test]
@ -103,32 +89,11 @@ fn exec_raw_ident() -> Result<(), ()> {
expect_success("examples/raw_ident.er") 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] #[test]
fn exec_record() -> Result<(), ()> { fn exec_record() -> Result<(), ()> {
expect_success("examples/record.er") 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] #[test]
fn exec_trait() -> Result<(), ()> { fn exec_trait() -> Result<(), ()> {
expect_success("examples/trait.er") expect_success("examples/trait.er")
@ -154,6 +119,51 @@ fn exec_with() -> Result<(), ()> {
expect_success("examples/with.er") 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<(), ()> { fn expect_success(file_path: &'static str) -> Result<(), ()> {
match exec_vm(file_path) { match exec_vm(file_path) {
Ok(0) => Ok(()), Ok(0) => Ok(()),