fix: type inference failure of as operator

Now the `as` operator is a kind of type ascription, i.e., a special operator.
This commit is contained in:
Shunsuke Shibayama 2023-04-26 23:41:35 +09:00
parent 6cb4e75fea
commit b3e09f213f
23 changed files with 435 additions and 318 deletions

View file

@ -475,6 +475,7 @@ impl<Checker: BuildRunnable> Server<Checker> {
self.file_cache.incremental_update(params.clone()); self.file_cache.incremental_update(params.clone());
if self.opt_features.contains(&OptionalFeatures::CheckOnType) { if self.opt_features.contains(&OptionalFeatures::CheckOnType) {
let uri = NormalizedUrl::new(params.text_document.uri); let uri = NormalizedUrl::new(params.text_document.uri);
// TODO: reset mutable dependent types
self.quick_check_file(uri)?; self.quick_check_file(uri)?;
} }
Ok(()) Ok(())

View file

@ -1464,11 +1464,6 @@ impl PyCodeGenerator {
self.emit_push_null(); self.emit_push_null();
self.emit_load_name_instr(Identifier::private("#in_operator")); self.emit_load_name_instr(Identifier::private("#in_operator"));
} }
// (x as T) == x
TokenKind::AsOp => {
self.emit_expr(*bin.lhs);
return;
}
_ => {} _ => {}
} }
let lhs_t = bin let lhs_t = bin

View file

@ -113,8 +113,8 @@ impl Context {
return true; return true;
} }
(l, r) if l.has_unbound_var() || r.has_unbound_var() => { (l, r) if l.has_unbound_var() || r.has_unbound_var() => {
let lt = self.get_tp_t(l).unwrap(); let Ok(lt) = self.get_tp_t(l) else { return false; };
let rt = self.get_tp_t(r).unwrap(); let Ok(rt) = self.get_tp_t(r) else { return false };
return self.same_type_of(&lt, &rt); return self.same_type_of(&lt, &rt);
} }
_ => {} _ => {}

View file

@ -618,10 +618,6 @@ impl Context {
let op_t = bin_op(I, T, Bool).quantify(); let op_t = bin_op(I, T, Bool).quantify();
self.register_builtin_erg_impl(OP_IN, op_t.clone(), Const, Visibility::BUILTIN_PRIVATE); self.register_builtin_erg_impl(OP_IN, op_t.clone(), Const, Visibility::BUILTIN_PRIVATE);
self.register_builtin_erg_impl(OP_NOT_IN, op_t, Const, Visibility::BUILTIN_PRIVATE); self.register_builtin_erg_impl(OP_NOT_IN, op_t, Const, Visibility::BUILTIN_PRIVATE);
let Sub = mono_q(TY_SUB, instanceof(Type));
let Sup = mono_q(TY_SUP, supertypeof(Sub.clone()));
let op_t = bin_op(Sub, tp_enum(Type, set! { ty_tp(Sup.clone()) }), Sup).quantify();
self.register_builtin_erg_impl(OP_AS, op_t, Const, Visibility::BUILTIN_PRIVATE);
/* unary */ /* unary */
// TODO: +/- Bool would like to be warned // TODO: +/- Bool would like to be warned
let M = mono_q(TY_M, subtypeof(mono(MUTIZABLE))); let M = mono_q(TY_M, subtypeof(mono(MUTIZABLE)));

View file

@ -346,7 +346,6 @@ const OP_DIV: &str = "__div__";
const OP_FLOOR_DIV: &str = "__floordiv__"; const OP_FLOOR_DIV: &str = "__floordiv__";
const OP_ABS: &str = "__abs__"; const OP_ABS: &str = "__abs__";
const OP_PARTIAL_CMP: &str = "__partial_cmp__"; const OP_PARTIAL_CMP: &str = "__partial_cmp__";
const OP_AS: &str = "__as__";
const OP_AND: &str = "__and__"; const OP_AND: &str = "__and__";
const OP_OR: &str = "__or__"; const OP_OR: &str = "__or__";
const OP_POW: &str = "__pow__"; const OP_POW: &str = "__pow__";
@ -406,8 +405,6 @@ const TY_L: &str = "L";
const TY_N: &str = "N"; const TY_N: &str = "N";
const TY_M: &str = "M"; const TY_M: &str = "M";
const TY_O: &str = "O"; const TY_O: &str = "O";
const TY_SUB: &str = "Sub";
const TY_SUP: &str = "Sup";
const KW_OLD: &str = "old"; const KW_OLD: &str = "old";
const KW_B: &str = "b"; const KW_B: &str = "b";

View file

@ -128,23 +128,22 @@ impl Context {
Ok(()) Ok(())
} }
TypeBoundSpec::NonDefault { lhs, spec } => { TypeBoundSpec::NonDefault { lhs, spec } => {
let constr = let constr = match spec.op.kind {
match spec.op.kind { TokenKind::SubtypeOf => Constraint::new_subtype_of(
TokenKind::SubtypeOf => Constraint::new_subtype_of( self.instantiate_typespec_full(&spec.t_spec, None, tv_cache, mode, true)?,
self.instantiate_typespec(&spec.t_spec, None, tv_cache, mode, true)?, ),
), TokenKind::SupertypeOf => Constraint::new_supertype_of(
TokenKind::SupertypeOf => Constraint::new_supertype_of( self.instantiate_typespec_full(&spec.t_spec, None, tv_cache, mode, true)?,
self.instantiate_typespec(&spec.t_spec, None, tv_cache, mode, true)?, ),
), TokenKind::Colon => Constraint::new_type_of(self.instantiate_typespec_full(
TokenKind::Colon => Constraint::new_type_of(self.instantiate_typespec( &spec.t_spec,
&spec.t_spec, None,
None, tv_cache,
tv_cache, mode,
mode, true,
true, )?),
)?), _ => unreachable!(),
_ => unreachable!(), };
};
if constr.get_sub_sup().is_none() { if constr.get_sub_sup().is_none() {
let tp = TyParam::named_free_var(lhs.inspect().clone(), self.level, constr); let tp = TyParam::named_free_var(lhs.inspect().clone(), self.level, constr);
tv_cache.push_or_init_typaram(lhs.inspect(), &tp, self); tv_cache.push_or_init_typaram(lhs.inspect(), &tp, self);
@ -205,7 +204,7 @@ impl Context {
) -> TyCheckResult<Type> { ) -> TyCheckResult<Type> {
let mut tmp_tv_cache = TyVarCache::new(self.level, self); let mut tmp_tv_cache = TyVarCache::new(self.level, self);
let spec_t = if let Some(t_spec) = t_spec { let spec_t = if let Some(t_spec) = t_spec {
self.instantiate_typespec(t_spec, None, &mut tmp_tv_cache, mode, false)? self.instantiate_typespec_full(t_spec, None, &mut tmp_tv_cache, mode, false)?
} else { } else {
free_var(self.level, Constraint::new_type_of(Type)) free_var(self.level, Constraint::new_type_of(Type))
}; };
@ -303,7 +302,7 @@ impl Context {
let opt_decl_t = opt_decl_sig_t let opt_decl_t = opt_decl_sig_t
.as_ref() .as_ref()
.map(|subr| ParamTy::Pos(subr.return_t.as_ref().clone())); .map(|subr| ParamTy::Pos(subr.return_t.as_ref().clone()));
match self.instantiate_typespec( match self.instantiate_typespec_full(
t_spec, t_spec,
opt_decl_t.as_ref(), opt_decl_t.as_ref(),
&mut tmp_tv_cache, &mut tmp_tv_cache,
@ -355,7 +354,13 @@ impl Context {
free_var(level, Constraint::new_type_of(Type)) free_var(level, Constraint::new_type_of(Type))
}; };
let spec_t = if let Some(spec_with_op) = &sig.t_spec { let spec_t = if let Some(spec_with_op) = &sig.t_spec {
self.instantiate_typespec(&spec_with_op.t_spec, opt_decl_t, tmp_tv_cache, mode, false)? self.instantiate_typespec_full(
&spec_with_op.t_spec,
opt_decl_t,
tmp_tv_cache,
mode,
false,
)?
} else { } else {
match &sig.pat { match &sig.pat {
ast::ParamPattern::Lit(lit) => v_enum(set![self.eval_lit(lit)?]), ast::ParamPattern::Lit(lit) => v_enum(set![self.eval_lit(lit)?]),
@ -589,6 +594,13 @@ impl Context {
line!(), line!(),
)) ))
}), }),
"True" | "False" | "None" => Err(TyCheckErrors::from(TyCheckError::not_a_type_error(
self.cfg.input.clone(),
line!() as usize,
simple.loc(),
self.caused_by(),
simple.ident.inspect(),
))),
other if simple.args.is_empty() => { other if simple.args.is_empty() => {
if let Some(TyParam::Type(t)) = self.get_tp_from_tv_cache(other, tmp_tv_cache) { if let Some(TyParam::Type(t)) = self.get_tp_from_tv_cache(other, tmp_tv_cache) {
return Ok(*t); return Ok(*t);
@ -638,14 +650,16 @@ impl Context {
}; };
// FIXME: kw args // FIXME: kw args
let mut new_params = vec![]; let mut new_params = vec![];
for (i, arg) in simple.args.pos_args().enumerate() { for ((i, arg), (name, param_vi)) in
let params = self.instantiate_const_expr( simple.args.pos_args().enumerate().zip(ctx.params.iter())
{
let param = self.instantiate_const_expr(
&arg.expr, &arg.expr,
Some((ctx, i)), Some((ctx, i)),
tmp_tv_cache, tmp_tv_cache,
not_found_is_qvar, not_found_is_qvar,
); );
let params = params.or_else(|e| { let param = param.or_else(|e| {
if not_found_is_qvar { if not_found_is_qvar {
let name = arg.expr.to_string(); let name = arg.expr.to_string();
// FIXME: handle `::` as a right way // FIXME: handle `::` as a right way
@ -661,7 +675,23 @@ impl Context {
Err(e) Err(e)
} }
})?; })?;
new_params.push(params); let arg_t = self.get_tp_t(&param).unwrap_or(Obj);
if self.subtype_of(&arg_t, &param_vi.t) {
new_params.push(param);
} else {
return Err(TyCheckErrors::from(TyCheckError::type_mismatch_error(
self.cfg.input.clone(),
line!() as usize,
arg.expr.loc(),
self.caused_by(),
name.as_ref().map_or("", |n| &n.inspect()[..]),
Some(i),
&param_vi.t,
&arg_t,
None,
None,
)));
}
} }
// FIXME: non-builtin // FIXME: non-builtin
Ok(poly(Str::rc(other), new_params)) Ok(poly(Str::rc(other), new_params))
@ -942,7 +972,7 @@ impl Context {
tmp_tv_cache, tmp_tv_cache,
not_found_is_qvar, not_found_is_qvar,
)?; )?;
let spec_t = self.instantiate_typespec( let spec_t = self.instantiate_typespec_full(
&tasc.t_spec.t_spec, &tasc.t_spec.t_spec,
None, None,
tmp_tv_cache, tmp_tv_cache,
@ -1041,12 +1071,12 @@ impl Context {
tmp_tv_cache: &mut TyVarCache, tmp_tv_cache: &mut TyVarCache,
mode: RegistrationMode, mode: RegistrationMode,
) -> TyCheckResult<ParamTy> { ) -> TyCheckResult<ParamTy> {
let t = self.instantiate_typespec(&p.ty, opt_decl_t, tmp_tv_cache, mode, false)?; let t = self.instantiate_typespec_full(&p.ty, opt_decl_t, tmp_tv_cache, mode, false)?;
if let Some(default_t) = default_t { if let Some(default_t) = default_t {
Ok(ParamTy::kw_default( Ok(ParamTy::kw_default(
p.name.as_ref().unwrap().inspect().to_owned(), p.name.as_ref().unwrap().inspect().to_owned(),
t, t,
self.instantiate_typespec(default_t, opt_decl_t, tmp_tv_cache, mode, false)?, self.instantiate_typespec_full(default_t, opt_decl_t, tmp_tv_cache, mode, false)?,
)) ))
} else { } else {
Ok(ParamTy::pos_or_kw( Ok(ParamTy::pos_or_kw(
@ -1056,7 +1086,7 @@ impl Context {
} }
} }
pub(crate) fn instantiate_typespec( pub(crate) fn instantiate_typespec_full(
&self, &self,
t_spec: &TypeSpec, t_spec: &TypeSpec,
opt_decl_t: Option<&ParamTy>, opt_decl_t: Option<&ParamTy>,
@ -1073,14 +1103,14 @@ impl Context {
not_found_is_qvar, not_found_is_qvar,
)?), )?),
TypeSpec::And(lhs, rhs) => Ok(self.intersection( TypeSpec::And(lhs, rhs) => Ok(self.intersection(
&self.instantiate_typespec( &self.instantiate_typespec_full(
lhs, lhs,
opt_decl_t, opt_decl_t,
tmp_tv_cache, tmp_tv_cache,
mode, mode,
not_found_is_qvar, not_found_is_qvar,
)?, )?,
&self.instantiate_typespec( &self.instantiate_typespec_full(
rhs, rhs,
opt_decl_t, opt_decl_t,
tmp_tv_cache, tmp_tv_cache,
@ -1089,14 +1119,14 @@ impl Context {
)?, )?,
)), )),
TypeSpec::Or(lhs, rhs) => Ok(self.union( TypeSpec::Or(lhs, rhs) => Ok(self.union(
&self.instantiate_typespec( &self.instantiate_typespec_full(
lhs, lhs,
opt_decl_t, opt_decl_t,
tmp_tv_cache, tmp_tv_cache,
mode, mode,
not_found_is_qvar, not_found_is_qvar,
)?, )?,
&self.instantiate_typespec( &self.instantiate_typespec_full(
rhs, rhs,
opt_decl_t, opt_decl_t,
tmp_tv_cache, tmp_tv_cache,
@ -1104,7 +1134,7 @@ impl Context {
not_found_is_qvar, not_found_is_qvar,
)?, )?,
)), )),
TypeSpec::Not(ty) => Ok(self.complement(&self.instantiate_typespec( TypeSpec::Not(ty) => Ok(self.complement(&self.instantiate_typespec_full(
ty, ty,
opt_decl_t, opt_decl_t,
tmp_tv_cache, tmp_tv_cache,
@ -1112,7 +1142,7 @@ impl Context {
not_found_is_qvar, not_found_is_qvar,
)?)), )?)),
TypeSpec::Array(arr) => { TypeSpec::Array(arr) => {
let elem_t = self.instantiate_typespec( let elem_t = self.instantiate_typespec_full(
&arr.ty, &arr.ty,
opt_decl_t, opt_decl_t,
tmp_tv_cache, tmp_tv_cache,
@ -1127,7 +1157,7 @@ impl Context {
Ok(array_t(elem_t, len)) Ok(array_t(elem_t, len))
} }
TypeSpec::SetWithLen(set) => { TypeSpec::SetWithLen(set) => {
let elem_t = self.instantiate_typespec( let elem_t = self.instantiate_typespec_full(
&set.ty, &set.ty,
opt_decl_t, opt_decl_t,
tmp_tv_cache, tmp_tv_cache,
@ -1144,7 +1174,7 @@ impl Context {
TypeSpec::Tuple(tup) => { TypeSpec::Tuple(tup) => {
let mut inst_tys = vec![]; let mut inst_tys = vec![];
for spec in tup.tys.iter() { for spec in tup.tys.iter() {
inst_tys.push(self.instantiate_typespec( inst_tys.push(self.instantiate_typespec_full(
spec, spec,
opt_decl_t, opt_decl_t,
tmp_tv_cache, tmp_tv_cache,
@ -1158,14 +1188,14 @@ impl Context {
let mut inst_tys = dict! {}; let mut inst_tys = dict! {};
for (k, v) in dict { for (k, v) in dict {
inst_tys.insert( inst_tys.insert(
self.instantiate_typespec( self.instantiate_typespec_full(
k, k,
opt_decl_t, opt_decl_t,
tmp_tv_cache, tmp_tv_cache,
mode, mode,
not_found_is_qvar, not_found_is_qvar,
)?, )?,
self.instantiate_typespec( self.instantiate_typespec_full(
v, v,
opt_decl_t, opt_decl_t,
tmp_tv_cache, tmp_tv_cache,
@ -1181,7 +1211,7 @@ impl Context {
for (k, v) in rec { for (k, v) in rec {
inst_tys.insert( inst_tys.insert(
self.instantiate_field(k)?, self.instantiate_field(k)?,
self.instantiate_typespec( self.instantiate_typespec_full(
v, v,
opt_decl_t, opt_decl_t,
tmp_tv_cache, tmp_tv_cache,
@ -1261,7 +1291,7 @@ impl Context {
})? })?
.into_iter() .into_iter()
.collect(); .collect();
let return_t = self.instantiate_typespec( let return_t = self.instantiate_typespec_full(
&subr.return_t, &subr.return_t,
opt_decl_t, opt_decl_t,
tmp_tv_ctx, tmp_tv_ctx,
@ -1286,12 +1316,23 @@ impl Context {
} }
} }
pub fn instantiate_field(&self, ident: &Identifier) -> TyCheckResult<Field> { pub(crate) fn instantiate_typespec(&self, t_spec: &ast::TypeSpec) -> TyCheckResult<Type> {
let mut dummy_tv_cache = TyVarCache::new(self.level, self);
self.instantiate_typespec_full(
t_spec,
None,
&mut dummy_tv_cache,
RegistrationMode::Normal,
false,
)
}
pub(crate) fn instantiate_field(&self, ident: &Identifier) -> TyCheckResult<Field> {
let vis = self.instantiate_vis_modifier(&ident.vis)?; let vis = self.instantiate_vis_modifier(&ident.vis)?;
Ok(Field::new(vis, ident.inspect().clone())) Ok(Field::new(vis, ident.inspect().clone()))
} }
pub fn instantiate_vis_modifier( pub(crate) fn instantiate_vis_modifier(
&self, &self,
spec: &VisModifierSpec, spec: &VisModifierSpec,
) -> TyCheckResult<VisibilityModifier> { ) -> TyCheckResult<VisibilityModifier> {
@ -1331,14 +1372,7 @@ impl Context {
Ok(VisibilityModifier::Restricted(namespaces)) Ok(VisibilityModifier::Restricted(namespaces))
} }
VisRestriction::SubtypeOf(typ) => { VisRestriction::SubtypeOf(typ) => {
let mut dummy_tv_cache = TyVarCache::new(self.level, self); let t = self.instantiate_typespec(typ)?;
let t = self.instantiate_typespec(
typ,
None,
&mut dummy_tv_cache,
RegistrationMode::Normal,
false,
)?;
Ok(VisibilityModifier::SubtypeRestricted(t)) Ok(VisibilityModifier::SubtypeRestricted(t))
} }
}, },
@ -1347,9 +1381,7 @@ impl Context {
pub(crate) fn expr_to_type(&self, expr: ast::Expr) -> Option<Type> { pub(crate) fn expr_to_type(&self, expr: ast::Expr) -> Option<Type> {
let t_spec = Parser::expr_to_type_spec(expr).ok()?; let t_spec = Parser::expr_to_type_spec(expr).ok()?;
let mut dummy = TyVarCache::new(self.level, self); self.instantiate_typespec(&t_spec).ok()
self.instantiate_typespec(&t_spec, None, &mut dummy, RegistrationMode::Normal, false)
.ok()
} }
pub(crate) fn expr_to_value(&self, expr: ast::Expr) -> Option<ValueObj> { pub(crate) fn expr_to_value(&self, expr: ast::Expr) -> Option<ValueObj> {

View file

@ -143,7 +143,8 @@ impl Context {
}; };
let vis = self.instantiate_vis_modifier(&ident.vis)?; let vis = self.instantiate_vis_modifier(&ident.vis)?;
let kind = id.map_or(VarKind::Declared, VarKind::Defined); let kind = id.map_or(VarKind::Declared, VarKind::Defined);
let sig_t = self.instantiate_var_sig_t(sig.t_spec.as_ref(), PreRegister)?; let sig_t =
self.instantiate_var_sig_t(sig.t_spec.as_ref().map(|ts| &ts.t_spec), PreRegister)?;
let py_name = if let ContextKind::PatchMethodDefs(_base) = &self.kind { let py_name = if let ContextKind::PatchMethodDefs(_base) = &self.kind {
Some(Str::from(format!("::{}{}", self.name, ident))) Some(Str::from(format!("::{}{}", self.name, ident)))
} else { } else {
@ -295,8 +296,16 @@ impl Context {
} else { } else {
VarKind::Defined(id) VarKind::Defined(id)
}; };
let t = sig.t_spec.as_ref().map_or(body_t.clone(), |ts| {
if ts.ascription_kind().is_force_cast() {
self.instantiate_typespec(&ts.t_spec)
.unwrap_or(body_t.clone())
} else {
body_t.clone()
}
});
let vi = VarInfo::new( let vi = VarInfo::new(
body_t.clone(), t,
muty, muty,
Visibility::new(vis, self.name.clone()), Visibility::new(vis, self.name.clone()),
kind, kind,
@ -902,7 +911,7 @@ impl Context {
if let Some(spec) = sig.return_t_spec.as_ref() { if let Some(spec) = sig.return_t_spec.as_ref() {
let mut dummy_tv_cache = TyVarCache::new(self.level, self); let mut dummy_tv_cache = TyVarCache::new(self.level, self);
let spec_t = self let spec_t = self
.instantiate_typespec( .instantiate_typespec_full(
spec, spec,
None, None,
&mut dummy_tv_cache, &mut dummy_tv_cache,
@ -944,8 +953,8 @@ impl Context {
if let Some(spec) = sig.t_spec.as_ref() { if let Some(spec) = sig.t_spec.as_ref() {
let mut dummy_tv_cache = TyVarCache::new(self.level, self); let mut dummy_tv_cache = TyVarCache::new(self.level, self);
let spec_t = self let spec_t = self
.instantiate_typespec( .instantiate_typespec_full(
spec, &spec.t_spec,
None, None,
&mut dummy_tv_cache, &mut dummy_tv_cache,
PreRegister, PreRegister,

View file

@ -689,13 +689,15 @@ impl Context {
let (l, r) = union.union_pair().unwrap_or((lsub, rsub)); let (l, r) = union.union_pair().unwrap_or((lsub, rsub));
let unified = self.unify(&l, &r); let unified = self.unify(&l, &r);
if unified.is_none() { if unified.is_none() {
let maybe_sub = self.readable_type(maybe_sub.clone());
let union = self.readable_type(union);
return Err(TyCheckErrors::from(TyCheckError::implicit_widening_error( return Err(TyCheckErrors::from(TyCheckError::implicit_widening_error(
self.cfg.input.clone(), self.cfg.input.clone(),
line!() as usize, line!() as usize,
loc.loc(), loc.loc(),
self.caused_by(), self.caused_by(),
maybe_sub, &maybe_sub,
maybe_sup, &union,
))); )));
} }
} }
@ -800,14 +802,16 @@ impl Context {
let (l, r) = new_sub.union_pair().unwrap_or((maybe_sub.clone(), sub)); let (l, r) = new_sub.union_pair().unwrap_or((maybe_sub.clone(), sub));
let unified = self.unify(&l, &r); let unified = self.unify(&l, &r);
if unified.is_none() { if unified.is_none() {
let maybe_sub = self.readable_type(maybe_sub.clone());
let new_sub = self.readable_type(new_sub);
return Err(TyCheckErrors::from( return Err(TyCheckErrors::from(
TyCheckError::implicit_widening_error( TyCheckError::implicit_widening_error(
self.cfg.input.clone(), self.cfg.input.clone(),
line!() as usize, line!() as usize,
loc.loc(), loc.loc(),
self.caused_by(), self.caused_by(),
maybe_sub, &maybe_sub,
maybe_sup, &new_sub,
), ),
)); ));
} }

View file

@ -1,9 +1,8 @@
use erg_common::traits::{Locational, Runnable, Stream}; use erg_common::traits::{Locational, Runnable, Stream};
use erg_common::{enum_unwrap, fn_name, log, set, Str}; use erg_common::{enum_unwrap, fn_name, log, set, Str};
use erg_parser::ast::{self, Identifier, VarName, AST}; use erg_parser::ast::{self, AscriptionKind, Identifier, VarName, AST};
use crate::context::instantiate::TyVarCache;
use crate::lower::ASTLowerer; use crate::lower::ASTLowerer;
use crate::ty::constructors::{mono, v_enum}; use crate::ty::constructors::{mono, v_enum};
use crate::ty::free::HasLevel; use crate::ty::free::HasLevel;
@ -11,7 +10,6 @@ use crate::ty::value::{GenTypeObj, TypeObj, ValueObj};
use crate::ty::{HasType, Type, Visibility}; use crate::ty::{HasType, Type, Visibility};
use crate::compile::AccessKind; use crate::compile::AccessKind;
use crate::context::RegistrationMode;
use crate::error::{LowerError, LowerErrors, LowerResult}; use crate::error::{LowerError, LowerErrors, LowerResult};
use crate::hir; use crate::hir;
use crate::hir::HIR; use crate::hir::HIR;
@ -33,15 +31,7 @@ impl ASTLowerer {
))); )));
} }
let opt_spec_t = if let Some(t_spec) = &sig.t_spec { let opt_spec_t = if let Some(t_spec) = &sig.t_spec {
let mut dummy_tv_cache = let t = self.module.context.instantiate_typespec(&t_spec.t_spec)?;
TyVarCache::new(self.module.context.level, &self.module.context);
let t = self.module.context.instantiate_typespec(
t_spec,
None,
&mut dummy_tv_cache,
RegistrationMode::Normal,
false,
)?;
t.lift(); t.lift();
Some(self.module.context.generalize_t(t)) Some(self.module.context.generalize_t(t))
} else { } else {
@ -97,7 +87,14 @@ impl ASTLowerer {
ident.vi.t = t; ident.vi.t = t;
ident.vi.py_name = py_name; ident.vi.py_name = py_name;
ident.vi.def_loc = self.module.context.absolutize(ident.raw.name.loc()); ident.vi.def_loc = self.module.context.absolutize(ident.raw.name.loc());
let sig = hir::VarSignature::new(ident, sig.t_spec); let t_spec = if let Some(ts) = sig.t_spec {
let spec_t = self.module.context.instantiate_typespec(&ts.t_spec)?;
let expr = self.fake_lower_expr(*ts.t_spec_as_expr.clone())?;
Some(hir::TypeSpecWithOp::new(ts, expr, spec_t))
} else {
None
};
let sig = hir::VarSignature::new(ident, t_spec);
let body = hir::DefBody::new(body.op, hir::Block::new(vec![chunk]), body.id); let body = hir::DefBody::new(body.op, hir::Block::new(vec![chunk]), body.id);
Ok(hir::Def::new(hir::Signature::Var(sig), body)) Ok(hir::Def::new(hir::Signature::Var(sig), body))
} }
@ -262,7 +259,13 @@ impl ASTLowerer {
ast::Signature::Var(var) => { ast::Signature::Var(var) => {
let ident = var.ident().unwrap().clone(); let ident = var.ident().unwrap().clone();
let ident = hir::Identifier::bare(ident); let ident = hir::Identifier::bare(ident);
let sig = hir::VarSignature::new(ident, var.t_spec); let t_spec = if let Some(ts) = var.t_spec {
let expr = self.fake_lower_expr(*ts.t_spec_as_expr.clone())?;
Some(hir::TypeSpecWithOp::new(ts, expr, Type::Failure))
} else {
None
};
let sig = hir::VarSignature::new(ident, t_spec);
Ok(hir::Signature::Var(sig)) Ok(hir::Signature::Var(sig))
} }
ast::Signature::Subr(subr) => { ast::Signature::Subr(subr) => {
@ -312,12 +315,17 @@ impl ASTLowerer {
elems, elems,
))) )))
} }
other => Err(LowerErrors::from(LowerError::declare_error( ast::Set::WithLength(set) => {
self.cfg().input.clone(), let len = self.fake_lower_expr(*set.len)?;
line!() as usize, let elem = self.fake_lower_expr(set.elem.expr)?;
other.loc(), Ok(hir::Set::WithLength(hir::SetWithLength::new(
self.module.context.caused_by(), set.l_brace,
))), set.r_brace,
Type::Failure,
len,
elem,
)))
}
} }
} }
@ -404,17 +412,12 @@ impl ASTLowerer {
fn fake_lower_type_asc(&self, tasc: ast::TypeAscription) -> LowerResult<hir::TypeAscription> { fn fake_lower_type_asc(&self, tasc: ast::TypeAscription) -> LowerResult<hir::TypeAscription> {
let expr = self.fake_lower_expr(*tasc.expr)?; let expr = self.fake_lower_expr(*tasc.expr)?;
let t_spec_as_expr = self.fake_lower_expr(*tasc.t_spec.t_spec_as_expr)?; let t_spec_as_expr = self.fake_lower_expr(*tasc.t_spec.t_spec_as_expr.clone())?;
let mut dummy_tv_cache = TyVarCache::new(self.module.context.level, &self.module.context); let spec_t = self
let spec_t = self.module.context.instantiate_typespec( .module
&tasc.t_spec.t_spec, .context
None, .instantiate_typespec(&tasc.t_spec.t_spec)?;
&mut dummy_tv_cache, let spec = hir::TypeSpecWithOp::new(tasc.t_spec, t_spec_as_expr, spec_t);
RegistrationMode::Normal,
false,
)?;
let spec =
hir::TypeSpecWithOp::new(tasc.t_spec.op, tasc.t_spec.t_spec, t_spec_as_expr, spec_t);
Ok(hir::TypeAscription::new(expr, spec)) Ok(hir::TypeAscription::new(expr, spec))
} }
@ -445,24 +448,26 @@ impl ASTLowerer {
fn declare_ident(&mut self, tasc: ast::TypeAscription) -> LowerResult<hir::TypeAscription> { fn declare_ident(&mut self, tasc: ast::TypeAscription) -> LowerResult<hir::TypeAscription> {
log!(info "entered {}({})", fn_name!(), tasc); log!(info "entered {}({})", fn_name!(), tasc);
let is_instance_ascription = tasc.is_instance_ascription(); let kind = tasc.kind();
let mut dummy_tv_cache = TyVarCache::new(self.module.context.level, &self.module.context);
match *tasc.expr { match *tasc.expr {
ast::Expr::Accessor(ast::Accessor::Ident(ident)) => { ast::Expr::Accessor(ast::Accessor::Ident(ident)) => {
let py_name = Str::rc(ident.inspect().trim_end_matches('!')); let py_name = Str::rc(ident.inspect().trim_end_matches('!'));
let t = self.module.context.instantiate_typespec( let t = self
&tasc.t_spec.t_spec, .module
None, .context
&mut dummy_tv_cache, .instantiate_typespec(&tasc.t_spec.t_spec)?;
RegistrationMode::Normal,
false,
)?;
t.lift(); t.lift();
let t = self.module.context.generalize_t(t); let t = self.module.context.generalize_t(t);
if is_instance_ascription { match kind {
self.declare_instance(&ident, &t, py_name.clone())?; AscriptionKind::TypeOf | AscriptionKind::AsCast => {
} else { self.declare_instance(&ident, &t, py_name.clone())?;
self.declare_subtype(&ident, &t)?; }
AscriptionKind::SubtypeOf => {
self.declare_subtype(&ident, &t)?;
}
_ => {
log!(err "supertype ascription is not supported yet");
}
} }
let muty = Mutability::from(&ident.inspect()[..]); let muty = Mutability::from(&ident.inspect()[..]);
let vis = self.module.context.instantiate_vis_modifier(&ident.vis)?; let vis = self.module.context.instantiate_vis_modifier(&ident.vis)?;
@ -477,24 +482,16 @@ impl ASTLowerer {
self.module.context.absolutize(ident.name.loc()), self.module.context.absolutize(ident.name.loc()),
); );
let ident = hir::Identifier::new(ident, None, vi); let ident = hir::Identifier::new(ident, None, vi);
let t_spec_expr = self.fake_lower_expr(*tasc.t_spec.t_spec_as_expr)?; let t_spec_expr = self.fake_lower_expr(*tasc.t_spec.t_spec_as_expr.clone())?;
let t_spec = hir::TypeSpecWithOp::new( let t_spec = hir::TypeSpecWithOp::new(tasc.t_spec, t_spec_expr, Type::Failure);
tasc.t_spec.op,
tasc.t_spec.t_spec,
t_spec_expr,
Type::Failure,
);
Ok(hir::Expr::Accessor(hir::Accessor::Ident(ident)).type_asc(t_spec)) Ok(hir::Expr::Accessor(hir::Accessor::Ident(ident)).type_asc(t_spec))
} }
ast::Expr::Accessor(ast::Accessor::Attr(attr)) => { ast::Expr::Accessor(ast::Accessor::Attr(attr)) => {
let py_name = Str::rc(attr.ident.inspect().trim_end_matches('!')); let py_name = Str::rc(attr.ident.inspect().trim_end_matches('!'));
let t = self.module.context.instantiate_typespec( let t = self
&tasc.t_spec.t_spec, .module
None, .context
&mut dummy_tv_cache, .instantiate_typespec(&tasc.t_spec.t_spec)?;
RegistrationMode::Normal,
false,
)?;
let ctx = self let ctx = self
.module .module
.context .context
@ -523,13 +520,8 @@ impl ASTLowerer {
); );
let ident = hir::Identifier::new(attr.ident, None, vi); let ident = hir::Identifier::new(attr.ident, None, vi);
let attr = obj.attr_expr(ident); let attr = obj.attr_expr(ident);
let t_spec_expr = self.fake_lower_expr(*tasc.t_spec.t_spec_as_expr)?; let t_spec_expr = self.fake_lower_expr(*tasc.t_spec.t_spec_as_expr.clone())?;
let t_spec = hir::TypeSpecWithOp::new( let t_spec = hir::TypeSpecWithOp::new(tasc.t_spec, t_spec_expr, Type::Failure);
tasc.t_spec.op,
tasc.t_spec.t_spec,
t_spec_expr,
Type::Failure,
);
Ok(attr.type_asc(t_spec)) Ok(attr.type_asc(t_spec))
} }
other => Err(LowerErrors::from(LowerError::declare_error( other => Err(LowerErrors::from(LowerError::declare_error(

View file

@ -368,6 +368,42 @@ impl LowerError {
) )
} }
pub fn not_a_type_error(
input: Input,
errno: usize,
loc: Location,
caused_by: String,
name: &str,
) -> Self {
let name = readable_name(name);
let hint = {
let n = StyledStr::new(name, Some(HINT), Some(ATTR));
Some(switch_lang!(
"japanese" => format!("{{{n}}}({n}のみを要素に持つ型)ではありませんか?"),
"simplified_chinese" => format!("{{{n}}}({n}的元素只有{n})是不是?"),
"traditional_chinese" => format!("{{{n}}}({n}的元素只有{n})是不是?"),
"english" => format!("Do you mean {{{n}}}, a type that has only {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!("{found} is not a type"),
),
errno,
TypeError,
loc,
),
input,
caused_by,
)
}
pub fn type_not_found( pub fn type_not_found(
input: Input, input: Input,
errno: usize, errno: usize,

View file

@ -1223,21 +1223,22 @@ passed keyword args: {kw_args_len}"
errno: usize, errno: usize,
loc: Location, loc: Location,
caused_by: String, caused_by: String,
expect: &Type, before: &Type,
found: &Type, after: &Type,
) -> Self { ) -> Self {
let maybe_sub_ = expect let before_ = before
.to_string() .to_string()
.with_color(erg_common::style::Color::Yellow); .with_color(erg_common::style::Color::Yellow);
let new_sub = found let after_ = after
.to_string() .to_string()
.with_color(erg_common::style::Color::Yellow); .with_color(erg_common::style::Color::Yellow);
let hint = switch_lang!( let hint = switch_lang!(
"japanese" => format!("{maybe_sub_}から{new_sub}への暗黙の型拡大はデフォルトでは禁止されています。明示的に型指定してください"), "japanese" => format!("{before_}から{after_}への暗黙の型拡大はデフォルトでは禁止されています。`as`などを使って明示的に型拡大してください"),
"simplified_chinese" => format!("隐式扩展{maybe_sub_}{new_sub}被默认禁止。请明确指定类型。"), "simplified_chinese" => format!("隐式扩展{before_}{after_}被默认禁止。请使用`as`显式扩展类型。"),
"traditional_chinese" => format!("隱式擴展{maybe_sub_}{new_sub}被默認禁止。請明確指定類型。"), "traditional_chinese" => format!("隱式擴展{before_}{after_}被默認禁止。請使用`as`顯式擴展類型。"),
"english" => format!("Implicitly widening {maybe_sub_} to {new_sub} is prohibited by default. Consider specifying the type explicitly."), "english" => format!("Implicitly widening {before_} to {after_} is prohibited by default. Consider widening the type explicitly using `as`."),
); );
// actually, this error will be overwritten, only `input`, `hint` and `loc` is useful
Self::type_mismatch_error( Self::type_mismatch_error(
input, input,
errno, errno,
@ -1245,8 +1246,8 @@ passed keyword args: {kw_args_len}"
caused_by, caused_by,
"", "",
None, None,
expect, &Type::Uninited,
found, &Type::Uninited,
None, None,
Some(hint), Some(hint),
) )

View file

@ -13,7 +13,7 @@ use erg_common::{
impl_nested_display_for_enum, impl_no_type_display_for_enum, impl_stream, impl_nested_display_for_enum, impl_no_type_display_for_enum, impl_stream,
}; };
use erg_parser::ast; use erg_parser::ast::{self, AscriptionKind};
use erg_parser::ast::{ use erg_parser::ast::{
fmt_lines, DefId, DefKind, OperationKind, TypeBoundSpecs, TypeSpec, VarName, fmt_lines, DefId, DefKind, OperationKind, TypeBoundSpecs, TypeSpec, VarName,
}; };
@ -1547,7 +1547,7 @@ impl Locational for Dummy {
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct VarSignature { pub struct VarSignature {
pub ident: Identifier, pub ident: Identifier,
pub t_spec: Option<TypeSpec>, pub t_spec: Option<TypeSpecWithOp>,
} }
impl NestedDisplay for VarSignature { impl NestedDisplay for VarSignature {
@ -1579,7 +1579,7 @@ impl HasType for VarSignature {
} }
impl VarSignature { impl VarSignature {
pub const fn new(ident: Identifier, t_spec: Option<TypeSpec>) -> Self { pub const fn new(ident: Identifier, t_spec: Option<TypeSpecWithOp>) -> Self {
Self { ident, t_spec } Self { ident, t_spec }
} }
@ -2006,7 +2006,7 @@ impl Signature {
pub fn t_spec(&self) -> Option<&TypeSpec> { pub fn t_spec(&self) -> Option<&TypeSpec> {
match self { match self {
Self::Var(v) => v.t_spec.as_ref(), Self::Var(v) => v.t_spec.as_ref().map(|t| &t.raw.t_spec),
Self::Subr(s) => s.return_t_spec.as_ref(), Self::Subr(s) => s.return_t_spec.as_ref(),
} }
} }
@ -2352,30 +2352,39 @@ impl ReDef {
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TypeSpecWithOp { pub struct TypeSpecWithOp {
pub op: Token, pub raw: ast::TypeSpecWithOp,
pub t_spec: TypeSpec, /// Required for dynamic type checking
pub t_spec_as_expr: Box<Expr>, pub expr: Box<Expr>,
pub spec_t: Type, pub spec_t: Type,
} }
impl NestedDisplay for TypeSpecWithOp { impl NestedDisplay for TypeSpecWithOp {
fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result { fn fmt_nest(&self, f: &mut fmt::Formatter<'_>, _level: usize) -> fmt::Result {
write!(f, "{} {}", self.op.content, self.t_spec) write!(f, "{}", self.raw)
} }
} }
impl_display_from_nested!(TypeSpecWithOp); impl_display_from_nested!(TypeSpecWithOp);
impl_locational!(TypeSpecWithOp, lossy op, t_spec); impl_locational!(TypeSpecWithOp, raw);
impl TypeSpecWithOp { impl TypeSpecWithOp {
pub fn new(op: Token, t_spec: TypeSpec, t_spec_as_expr: Expr, spec_t: Type) -> Self { pub fn new(raw: ast::TypeSpecWithOp, expr: Expr, spec_t: Type) -> Self {
Self { Self {
op, raw,
t_spec, expr: Box::new(expr),
t_spec_as_expr: Box::new(t_spec_as_expr),
spec_t, spec_t,
} }
} }
pub fn kind(&self) -> AscriptionKind {
match self.raw.op.kind {
TokenKind::Colon => AscriptionKind::TypeOf,
TokenKind::SubtypeOf => AscriptionKind::SubtypeOf,
TokenKind::SupertypeOf => AscriptionKind::SupertypeOf,
TokenKind::As => AscriptionKind::AsCast,
_ => unreachable!(),
}
}
} }
#[derive(Debug, Clone, PartialEq, Eq, Hash)] #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@ -2402,11 +2411,19 @@ impl_locational!(TypeAscription, expr, spec);
impl HasType for TypeAscription { impl HasType for TypeAscription {
#[inline] #[inline]
fn ref_t(&self) -> &Type { fn ref_t(&self) -> &Type {
self.expr.ref_t() if self.spec.kind().is_force_cast() {
&self.spec.spec_t
} else {
self.expr.ref_t()
}
} }
#[inline] #[inline]
fn ref_mut_t(&mut self) -> &mut Type { fn ref_mut_t(&mut self) -> &mut Type {
self.expr.ref_mut_t() if self.spec.kind().is_force_cast() {
&mut self.spec.spec_t
} else {
self.expr.ref_mut_t()
}
} }
#[inline] #[inline]
fn signature_t(&self) -> Option<&Type> { fn signature_t(&self) -> Option<&Type> {

View file

@ -14,7 +14,7 @@ use erg_common::traits::{ExitStatus, Locational, NoTypeDisplay, Runnable, Stream
use erg_common::triple::Triple; use erg_common::triple::Triple;
use erg_common::{fmt_option, fn_name, log, switch_lang, Str}; use erg_common::{fmt_option, fn_name, log, switch_lang, Str};
use erg_parser::ast::{self, VisModifierSpec}; use erg_parser::ast::{self, AscriptionKind, VisModifierSpec};
use erg_parser::ast::{OperationKind, TypeSpecWithOp, VarName, AST}; use erg_parser::ast::{OperationKind, TypeSpecWithOp, VarName, 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};
@ -1097,13 +1097,8 @@ impl ASTLowerer {
type_spec_with_op: ast::TypeSpecWithOp, type_spec_with_op: ast::TypeSpecWithOp,
spec_t: Type, spec_t: Type,
) -> LowerResult<hir::TypeSpecWithOp> { ) -> LowerResult<hir::TypeSpecWithOp> {
let expr = self.fake_lower_expr(*type_spec_with_op.t_spec_as_expr)?; let expr = self.fake_lower_expr(*type_spec_with_op.t_spec_as_expr.clone())?;
Ok(hir::TypeSpecWithOp::new( Ok(hir::TypeSpecWithOp::new(type_spec_with_op, expr, spec_t))
type_spec_with_op.op,
type_spec_with_op.t_spec,
expr,
spec_t,
))
} }
fn lower_params(&mut self, params: ast::Params) -> LowerResult<hir::Params> { fn lower_params(&mut self, params: ast::Params) -> LowerResult<hir::Params> {
@ -1419,7 +1414,7 @@ impl ASTLowerer {
.module .module
.context .context
.instantiate_var_sig_t( .instantiate_var_sig_t(
sig.t_spec.as_ref(), sig.t_spec.as_ref().map(|ts| &ts.t_spec),
RegistrationMode::PreRegister, RegistrationMode::PreRegister,
) )
.ok(); .ok();
@ -1453,7 +1448,14 @@ impl ASTLowerer {
None, None,
)?; )?;
let ident = hir::Identifier::new(ident, None, vi); let ident = hir::Identifier::new(ident, None, vi);
let sig = hir::VarSignature::new(ident, sig.t_spec); let t_spec = if let Some(ts) = sig.t_spec {
let spec_t = self.module.context.instantiate_typespec(&ts.t_spec)?;
let expr = self.fake_lower_expr(*ts.t_spec_as_expr.clone())?;
Some(hir::TypeSpecWithOp::new(ts, expr, spec_t))
} else {
None
};
let sig = hir::VarSignature::new(ident, t_spec);
let body = hir::DefBody::new(body.op, block, body.id); let body = hir::DefBody::new(body.op, block, body.id);
Ok(hir::Def::new(hir::Signature::Var(sig), body)) Ok(hir::Def::new(hir::Signature::Var(sig), body))
} }
@ -1703,7 +1705,7 @@ impl ASTLowerer {
let (impl_trait, t_spec) = match &args.pos_args().first().unwrap().expr { let (impl_trait, t_spec) = match &args.pos_args().first().unwrap().expr {
// TODO: check `tasc.op` // TODO: check `tasc.op`
ast::Expr::TypeAscription(tasc) => ( ast::Expr::TypeAscription(tasc) => (
self.module.context.instantiate_typespec( self.module.context.instantiate_typespec_full(
&tasc.t_spec.t_spec, &tasc.t_spec.t_spec,
None, None,
&mut dummy_tv_cache, &mut dummy_tv_cache,
@ -1724,7 +1726,7 @@ impl ASTLowerer {
} }
}; };
Ok(( Ok((
self.module.context.instantiate_typespec( self.module.context.instantiate_typespec_full(
spec, spec,
None, None,
&mut dummy_tv_cache, &mut dummy_tv_cache,
@ -1735,7 +1737,7 @@ impl ASTLowerer {
)) ))
} }
ast::TypeAppArgsKind::SubtypeOf(trait_spec) => { ast::TypeAppArgsKind::SubtypeOf(trait_spec) => {
let impl_trait = self.module.context.instantiate_typespec( let impl_trait = self.module.context.instantiate_typespec_full(
&trait_spec.t_spec, &trait_spec.t_spec,
None, None,
&mut dummy_tv_cache, &mut dummy_tv_cache,
@ -1743,7 +1745,7 @@ impl ASTLowerer {
false, false,
)?; )?;
Ok(( Ok((
self.module.context.instantiate_typespec( self.module.context.instantiate_typespec_full(
spec, spec,
None, None,
&mut dummy_tv_cache, &mut dummy_tv_cache,
@ -1756,7 +1758,7 @@ impl ASTLowerer {
} }
} }
other => Ok(( other => Ok((
self.module.context.instantiate_typespec( self.module.context.instantiate_typespec_full(
other, other,
None, None,
&mut dummy_tv_cache, &mut dummy_tv_cache,
@ -1776,15 +1778,7 @@ impl ASTLowerer {
}; };
let base_t_expr = call.args.get_left_or_key("Base").unwrap(); let base_t_expr = call.args.get_left_or_key("Base").unwrap();
let spec = Parser::expr_to_type_spec(base_t_expr.clone()).unwrap(); let spec = Parser::expr_to_type_spec(base_t_expr.clone()).unwrap();
let mut dummy_tv_cache = self.module.context.instantiate_typespec(&spec)?
TyVarCache::new(self.module.context.level, &self.module.context);
self.module.context.instantiate_typespec(
&spec,
None,
&mut dummy_tv_cache,
RegistrationMode::Normal,
false,
)?
}; };
let mut hir_def = self.lower_def(class_def.def)?; let mut hir_def = self.lower_def(class_def.def)?;
let base = Self::get_require_or_sup_or_base(hir_def.body.block.remove(0)).unwrap(); let base = Self::get_require_or_sup_or_base(hir_def.body.block.remove(0)).unwrap();
@ -2168,44 +2162,43 @@ impl ASTLowerer {
fn lower_type_asc(&mut self, tasc: ast::TypeAscription) -> LowerResult<hir::TypeAscription> { fn lower_type_asc(&mut self, tasc: ast::TypeAscription) -> LowerResult<hir::TypeAscription> {
log!(info "entered {}({tasc})", fn_name!()); log!(info "entered {}({tasc})", fn_name!());
let is_instance_ascription = tasc.is_instance_ascription(); let kind = tasc.kind();
let mut dummy_tv_cache = TyVarCache::new(self.module.context.level, &self.module.context); let spec_t = self
let spec_t = self.module.context.instantiate_typespec( .module
&tasc.t_spec.t_spec, .context
None, .instantiate_typespec(&tasc.t_spec.t_spec)?;
&mut dummy_tv_cache,
RegistrationMode::Normal,
false,
)?;
let expr = self.lower_expr(*tasc.expr)?; let expr = self.lower_expr(*tasc.expr)?;
if is_instance_ascription { match kind {
self.module.context.sub_unify( AscriptionKind::TypeOf | AscriptionKind::AsCast => {
expr.ref_t(), self.module.context.sub_unify(
&spec_t, expr.ref_t(),
&expr,
Some(&Str::from(expr.to_string())),
)?;
} else {
// if subtype ascription
let &ctx = self
.module
.context
.get_singular_ctxs_by_hir_expr(&expr, &self.module.context)?
.first()
.unwrap();
// REVIEW: need to use subtype_of?
if ctx.super_traits.iter().all(|trait_| trait_ != &spec_t)
&& ctx.super_classes.iter().all(|class| class != &spec_t)
{
return Err(LowerErrors::from(LowerError::subtyping_error(
self.cfg.input.clone(),
line!() as usize,
expr.ref_t(), // FIXME:
&spec_t, &spec_t,
Location::concat(&expr, &tasc.t_spec), &expr,
self.module.context.caused_by(), Some(&Str::from(expr.to_string())),
))); )?;
} }
AscriptionKind::SubtypeOf => {
let &ctx = self
.module
.context
.get_singular_ctxs_by_hir_expr(&expr, &self.module.context)?
.first()
.unwrap();
// REVIEW: need to use subtype_of?
if ctx.super_traits.iter().all(|trait_| trait_ != &spec_t)
&& ctx.super_classes.iter().all(|class| class != &spec_t)
{
return Err(LowerErrors::from(LowerError::subtyping_error(
self.cfg.input.clone(),
line!() as usize,
expr.ref_t(), // FIXME:
&spec_t,
Location::concat(&expr, &tasc.t_spec),
self.module.context.caused_by(),
)));
}
}
_ => {}
} }
let t_spec = self.lower_type_spec_with_op(tasc.t_spec, spec_t)?; let t_spec = self.lower_type_spec_with_op(tasc.t_spec, spec_t)?;
Ok(expr.type_asc(t_spec)) Ok(expr.type_asc(t_spec))
@ -2213,15 +2206,11 @@ impl ASTLowerer {
fn lower_decl(&mut self, tasc: ast::TypeAscription) -> LowerResult<hir::TypeAscription> { fn lower_decl(&mut self, tasc: ast::TypeAscription) -> LowerResult<hir::TypeAscription> {
log!(info "entered {}({tasc})", fn_name!()); log!(info "entered {}({tasc})", fn_name!());
let is_instance_ascription = tasc.is_instance_ascription(); let kind = tasc.kind();
let mut dummy_tv_cache = TyVarCache::new(self.module.context.level, &self.module.context); let spec_t = self
let spec_t = self.module.context.instantiate_typespec( .module
&tasc.t_spec.t_spec, .context
None, .instantiate_typespec(&tasc.t_spec.t_spec)?;
&mut dummy_tv_cache,
RegistrationMode::Normal,
false,
)?;
let ast::Expr::Accessor(ast::Accessor::Ident(ident)) = *tasc.expr else { let ast::Expr::Accessor(ast::Accessor::Ident(ident)) = *tasc.expr else {
return Err(LowerErrors::from(LowerError::syntax_error( return Err(LowerErrors::from(LowerError::syntax_error(
self.cfg.input.clone(), self.cfg.input.clone(),
@ -2270,22 +2259,28 @@ impl ASTLowerer {
similar_info, similar_info,
) )
})?; })?;
if is_instance_ascription { match kind {
self.module AscriptionKind::TypeOf | AscriptionKind::AsCast => {
.context self.module.context.sub_unify(
.sub_unify(&ident_vi.t, &spec_t, &ident, Some(ident.inspect()))?;
} else {
// if subtype ascription
if self.module.context.subtype_of(&ident_vi.t, &spec_t) {
return Err(LowerErrors::from(LowerError::subtyping_error(
self.cfg.input.clone(),
line!() as usize,
&ident_vi.t, &ident_vi.t,
&spec_t, &spec_t,
ident.loc(), &ident,
self.module.context.caused_by(), Some(ident.inspect()),
))); )?;
} }
AscriptionKind::SubtypeOf => {
if self.module.context.subtype_of(&ident_vi.t, &spec_t) {
return Err(LowerErrors::from(LowerError::subtyping_error(
self.cfg.input.clone(),
line!() as usize,
&ident_vi.t,
&spec_t,
ident.loc(),
self.module.context.caused_by(),
)));
}
}
_ => {}
} }
let qual_name = self let qual_name = self
.module .module

View file

@ -1909,7 +1909,6 @@ impl ConstTypeAsc {
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum ConstExpr { pub enum ConstExpr {
Lit(Literal), Lit(Literal),
Erased(Literal), // _
Accessor(ConstAccessor), Accessor(ConstAccessor),
App(ConstApp), App(ConstApp),
Array(ConstArray), Array(ConstArray),
@ -1924,9 +1923,9 @@ pub enum ConstExpr {
TypeAsc(ConstTypeAsc), TypeAsc(ConstTypeAsc),
} }
impl_nested_display_for_chunk_enum!(ConstExpr; Lit, Accessor, App, Array, Set, Dict, Tuple, Record, BinOp, UnaryOp, Def, Lambda, Erased, Set, TypeAsc); impl_nested_display_for_chunk_enum!(ConstExpr; Lit, Accessor, App, Array, Set, Dict, Tuple, Record, BinOp, UnaryOp, Def, Lambda, Set, TypeAsc);
impl_display_from_nested!(ConstExpr); impl_display_from_nested!(ConstExpr);
impl_locational_for_enum!(ConstExpr; Lit, Accessor, App, Array, Set, Dict, Tuple, Record, BinOp, UnaryOp, Def, Lambda, Erased, Set, TypeAsc); impl_locational_for_enum!(ConstExpr; Lit, Accessor, App, Array, Set, Dict, Tuple, Record, BinOp, UnaryOp, Def, Lambda, Set, TypeAsc);
impl ConstExpr { impl ConstExpr {
pub fn need_to_be_closed(&self) -> bool { pub fn need_to_be_closed(&self) -> bool {
@ -1948,7 +1947,6 @@ impl ConstExpr {
Self::BinOp(binop) => Expr::BinOp(binop.downcast()), Self::BinOp(binop) => Expr::BinOp(binop.downcast()),
Self::UnaryOp(unop) => Expr::UnaryOp(unop.downcast()), Self::UnaryOp(unop) => Expr::UnaryOp(unop.downcast()),
Self::TypeAsc(type_asc) => Expr::TypeAscription(type_asc.downcast()), Self::TypeAsc(type_asc) => Expr::TypeAscription(type_asc.downcast()),
Self::Erased(lit) => Expr::Dummy(Dummy::new(Some(lit.loc()), vec![])),
} }
} }
} }
@ -2529,6 +2527,7 @@ impl TypeSpec {
pub struct TypeSpecWithOp { pub struct TypeSpecWithOp {
pub op: Token, pub op: Token,
pub t_spec: TypeSpec, pub t_spec: TypeSpec,
/// Required for dynamic type checking
pub t_spec_as_expr: Box<Expr>, pub t_spec_as_expr: Box<Expr>,
} }
@ -2549,6 +2548,16 @@ impl TypeSpecWithOp {
t_spec_as_expr: Box::new(t_spec_as_expr), t_spec_as_expr: Box::new(t_spec_as_expr),
} }
} }
pub fn ascription_kind(&self) -> AscriptionKind {
match self.op.kind {
TokenKind::Colon => AscriptionKind::TypeOf,
TokenKind::SubtypeOf => AscriptionKind::SubtypeOf,
TokenKind::SupertypeOf => AscriptionKind::SupertypeOf,
TokenKind::As => AscriptionKind::AsCast,
kind => todo!("{kind}"),
}
}
} }
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
@ -3095,6 +3104,7 @@ impl VarRecordPattern {
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct VarDataPackPattern { pub struct VarDataPackPattern {
pub class: TypeSpec, pub class: TypeSpec,
pub class_as_expr: Box<Expr>,
pub args: VarRecordPattern, pub args: VarRecordPattern,
} }
@ -3107,8 +3117,12 @@ impl fmt::Display for VarDataPackPattern {
impl_locational!(VarDataPackPattern, class, args); impl_locational!(VarDataPackPattern, class, args);
impl VarDataPackPattern { impl VarDataPackPattern {
pub const fn new(class: TypeSpec, args: VarRecordPattern) -> Self { pub const fn new(class: TypeSpec, class_as_expr: Box<Expr>, args: VarRecordPattern) -> Self {
Self { class, args } Self {
class,
class_as_expr,
args,
}
} }
} }
@ -3185,7 +3199,7 @@ impl VarPattern {
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct VarSignature { pub struct VarSignature {
pub pat: VarPattern, pub pat: VarPattern,
pub t_spec: Option<TypeSpec>, pub t_spec: Option<TypeSpecWithOp>,
} }
impl NestedDisplay for VarSignature { impl NestedDisplay for VarSignature {
@ -3207,7 +3221,7 @@ impl Locational for VarSignature {
} }
impl VarSignature { impl VarSignature {
pub const fn new(pat: VarPattern, t_spec: Option<TypeSpec>) -> Self { pub const fn new(pat: VarPattern, t_spec: Option<TypeSpecWithOp>) -> Self {
Self { pat, t_spec } Self { pat, t_spec }
} }
@ -3860,7 +3874,7 @@ impl Signature {
pub fn t_spec(&self) -> Option<&TypeSpec> { pub fn t_spec(&self) -> Option<&TypeSpec> {
match self { match self {
Self::Var(v) => v.t_spec.as_ref(), Self::Var(v) => v.t_spec.as_ref().map(|t| &t.t_spec),
Self::Subr(c) => c.return_t_spec.as_ref(), Self::Subr(c) => c.return_t_spec.as_ref(),
} }
} }
@ -3884,6 +3898,20 @@ impl Signature {
} }
} }
#[derive(Debug, Clone, Copy)]
pub enum AscriptionKind {
TypeOf,
SubtypeOf,
SupertypeOf,
AsCast,
}
impl AscriptionKind {
pub const fn is_force_cast(&self) -> bool {
matches!(self, Self::AsCast)
}
}
/// type_ascription ::= expr ':' type /// type_ascription ::= expr ':' type
/// | expr '<:' type /// | expr '<:' type
/// | expr ':>' type /// | expr ':>' type
@ -3910,12 +3938,8 @@ impl TypeAscription {
} }
} }
pub fn is_instance_ascription(&self) -> bool { pub fn kind(&self) -> AscriptionKind {
self.t_spec.op.is(TokenKind::Colon) self.t_spec.ascription_kind()
}
pub fn is_subtype_ascription(&self) -> bool {
self.t_spec.op.is(TokenKind::SubtypeOf)
} }
} }

View file

@ -198,12 +198,12 @@ impl Parser {
pack: DataPack, pack: DataPack,
) -> ParseResult<VarDataPackPattern> { ) -> ParseResult<VarDataPackPattern> {
debug_call_info!(self); debug_call_info!(self);
let class = Self::expr_to_type_spec(*pack.class).map_err(|e| self.errs.push(e))?; let class = Self::expr_to_type_spec(*pack.class.clone()).map_err(|e| self.errs.push(e))?;
let args = self let args = self
.convert_record_to_record_pat(pack.args) .convert_record_to_record_pat(pack.args)
.map_err(|_| self.stack_dec(fn_name!()))?; .map_err(|_| self.stack_dec(fn_name!()))?;
debug_exit_info!(self); debug_exit_info!(self);
Ok(VarDataPackPattern::new(class, args)) Ok(VarDataPackPattern::new(class, pack.class, args))
} }
fn convert_tuple_to_tuple_pat(&mut self, tuple: Tuple) -> ParseResult<VarTuplePattern> { fn convert_tuple_to_tuple_pat(&mut self, tuple: Tuple) -> ParseResult<VarTuplePattern> {
@ -243,7 +243,7 @@ impl Parser {
.map_err(|_| self.stack_dec(fn_name!()))?; .map_err(|_| self.stack_dec(fn_name!()))?;
let sig = match sig { let sig = match sig {
Signature::Var(var) => { Signature::Var(var) => {
let var = VarSignature::new(var.pat, Some(tasc.t_spec.t_spec)); let var = VarSignature::new(var.pat, Some(tasc.t_spec));
Signature::Var(var) Signature::Var(var)
} }
Signature::Subr(subr) => { Signature::Subr(subr) => {

View file

@ -498,7 +498,11 @@ impl Desugarer {
todo!() todo!()
} }
fn gen_buf_name_and_sig(&mut self, line: u32, t_spec: Option<TypeSpec>) -> (String, Signature) { fn gen_buf_name_and_sig(
&mut self,
line: u32,
t_spec: Option<TypeSpecWithOp>,
) -> (String, Signature) {
let buf_name = fresh_varname(); let buf_name = fresh_varname();
let buf_sig = Signature::Var(VarSignature::new( let buf_sig = Signature::Var(VarSignature::new(
VarPattern::Ident(Identifier::private_with_line(Str::rc(&buf_name), line)), VarPattern::Ident(Identifier::private_with_line(Str::rc(&buf_name), line)),
@ -613,9 +617,14 @@ impl Desugarer {
} }
} }
VarPattern::DataPack(pack) => { VarPattern::DataPack(pack) => {
let t_spec = TypeSpecWithOp::new(
COLON,
pack.class.clone(),
*pack.class_as_expr.clone(),
);
let (buf_name, buf_sig) = self.gen_buf_name_and_sig( let (buf_name, buf_sig) = self.gen_buf_name_and_sig(
v.ln_begin().unwrap_or(1), v.ln_begin().unwrap_or(1),
Some(pack.class.clone()), // TODO: これだとvの型指定の意味がなくなる Some(t_spec), // TODO: これだとvの型指定の意味がなくなる
); );
let block = body let block = body
.block .block
@ -756,8 +765,10 @@ impl Desugarer {
} }
} }
VarPattern::DataPack(pack) => { VarPattern::DataPack(pack) => {
let (buf_name, buf_sig) = self let t_spec =
.gen_buf_name_and_sig(sig.ln_begin().unwrap_or(1), Some(pack.class.clone())); TypeSpecWithOp::new(COLON, pack.class.clone(), *pack.class_as_expr.clone());
let (buf_name, buf_sig) =
self.gen_buf_name_and_sig(sig.ln_begin().unwrap_or(1), Some(t_spec));
let buf_def = Def::new(buf_sig, body); let buf_def = Def::new(buf_sig, body);
new_module.push(Expr::Def(buf_def)); new_module.push(Expr::Def(buf_def));
for VarRecordAttr { lhs, rhs } in pack.args.attrs.iter() { for VarRecordAttr { lhs, rhs } in pack.args.attrs.iter() {
@ -1101,7 +1112,7 @@ impl Desugarer {
Expr::Def(Def::new( Expr::Def(Def::new(
Signature::Var(VarSignature::new( Signature::Var(VarSignature::new(
VarPattern::Ident(ident), VarPattern::Ident(ident),
sig.t_spec.as_ref().map(|ts| ts.t_spec.clone()), sig.t_spec.clone(),
)), )),
body, body,
)), )),
@ -1150,7 +1161,7 @@ impl Desugarer {
Expr::Def(Def::new( Expr::Def(Def::new(
Signature::Var(VarSignature::new( Signature::Var(VarSignature::new(
VarPattern::Ident(Identifier::private(Str::from(&buf_name))), VarPattern::Ident(Identifier::private(Str::from(&buf_name))),
sig.t_spec.as_ref().map(|ts| ts.t_spec.clone()), sig.t_spec.clone(),
)), )),
body, body,
)), )),
@ -1188,7 +1199,7 @@ impl Desugarer {
Expr::Def(Def::new( Expr::Def(Def::new(
Signature::Var(VarSignature::new( Signature::Var(VarSignature::new(
VarPattern::Ident(Identifier::private(Str::from(&buf_name))), VarPattern::Ident(Identifier::private(Str::from(&buf_name))),
sig.t_spec.as_ref().map(|ts| ts.t_spec.clone()), sig.t_spec.clone(),
)), )),
body, body,
)), )),
@ -1255,10 +1266,7 @@ impl Desugarer {
*/ */
ParamPattern::VarName(name) => { ParamPattern::VarName(name) => {
let ident = Identifier::new(VisModifierSpec::Private, name.clone()); let ident = Identifier::new(VisModifierSpec::Private, name.clone());
let v = VarSignature::new( let v = VarSignature::new(VarPattern::Ident(ident), sig.t_spec.clone());
VarPattern::Ident(ident),
sig.t_spec.as_ref().map(|ts| ts.t_spec.clone()),
);
let def = Def::new(Signature::Var(v), body); let def = Def::new(Signature::Var(v), body);
new_body.insert(insertion_idx, Expr::Def(def)); new_body.insert(insertion_idx, Expr::Def(def));
insertion_idx += 1; insertion_idx += 1;

View file

@ -700,7 +700,7 @@ impl Lexer /*<'a>*/ {
// e.g. and(true, true, true) = true // e.g. and(true, true, true) = true
let kind = match &cont[..] { let kind = match &cont[..] {
"and" => AndOp, "and" => AndOp,
"as" => AsOp, "as" => As,
"or" => OrOp, "or" => OrOp,
"in" => InOp, "in" => InOp,
"notin" => NotInOp, "notin" => NotInOp,

View file

@ -1209,7 +1209,7 @@ impl Parser {
// type ascription // type ascription
Some(op) Some(op)
if (op.is(Colon) && !self.nth_is(1, Newline)) if (op.is(Colon) && !self.nth_is(1, Newline))
|| (op.is(SubtypeOf) || op.is(SupertypeOf)) => || (op.is(SubtypeOf) || op.is(SupertypeOf) || op.is(As)) =>
{ {
// "a": 1 (key-value pair) // "a": 1 (key-value pair)
if in_brace { if in_brace {
@ -1480,7 +1480,7 @@ impl Parser {
// type ascription // type ascription
Some(op) Some(op)
if (op.is(Colon) && !self.nth_is(1, Newline)) if (op.is(Colon) && !self.nth_is(1, Newline))
|| (op.is(SubtypeOf) || op.is(SupertypeOf)) => || (op.is(SubtypeOf) || op.is(SupertypeOf) || op.is(As)) =>
{ {
// "a": 1 (key-value pair) // "a": 1 (key-value pair)
if in_brace { if in_brace {

View file

@ -108,8 +108,6 @@ pub enum TokenKind {
IsNotOp, IsNotOp,
/// `and` /// `and`
AndOp, AndOp,
/// `as`
AsOp,
/// `or` /// `or`
OrOp, OrOp,
/// `dot` (scalar product) /// `dot` (scalar product)
@ -156,6 +154,8 @@ pub enum TokenKind {
SupertypeOf, SupertypeOf,
/// <: /// <:
SubtypeOf, SubtypeOf,
/// `as`
As,
/// , /// ,
Comma, Comma,
/// ^ /// ^
@ -241,7 +241,7 @@ impl TokenKind {
TokenCategory::UnaryOp TokenCategory::UnaryOp
} }
Try => TokenCategory::PostfixOp, Try => TokenCategory::PostfixOp,
Comma | Colon | DblColon | SupertypeOf | SubtypeOf | Dot | Pipe | Walrus Comma | Colon | DblColon | SupertypeOf | SubtypeOf | As | Dot | Pipe | Walrus
| Inclusion => TokenCategory::SpecialBinOp, | Inclusion => TokenCategory::SpecialBinOp,
Assign => TokenCategory::DefOp, Assign => TokenCategory::DefOp,
FuncArrow | ProcArrow => TokenCategory::LambdaOp, FuncArrow | ProcArrow => TokenCategory::LambdaOp,
@ -271,16 +271,15 @@ impl TokenKind {
BitXor => 130, // ^^ BitXor => 130, // ^^
BitOr => 120, // || BitOr => 120, // ||
Closed | LeftOpen | RightOpen | Open => 100, // range operators Closed | LeftOpen | RightOpen | Open => 100, // range operators
Less | Gre | LessEq | GreEq | DblEq | NotEq | AsOp | InOp | NotInOp | IsOp Less | Gre | LessEq | GreEq | DblEq | NotEq | InOp | NotInOp | IsOp | IsNotOp => 90, // < > <= >= == != in notin is isnot
| IsNotOp => 90, // < > <= >= == != as in notin is isnot AndOp => 80, // and
AndOp => 80, // and OrOp => 70, // or
OrOp => 70, // or FuncArrow | ProcArrow | Inclusion => 60, // -> => <-
FuncArrow | ProcArrow | Inclusion => 60, // -> => <- Colon | SupertypeOf | SubtypeOf | As => 50, // : :> <: as
Colon | SupertypeOf | SubtypeOf => 50, // : :> <: Comma => 40, // ,
Comma => 40, // , Assign | Walrus => 20, // = :=
Assign | Walrus => 20, // = := Newline | Semi => 10, // \n ;
Newline | Semi => 10, // \n ; LParen | LBrace | LSqBr | Indent => 0, // ( { [ Indent
LParen | LBrace | LSqBr | Indent => 0, // ( { [ Indent
_ => return None, _ => return None,
}; };
Some(prec) Some(prec)

View file

@ -50,30 +50,6 @@ Generate anonymous functions, function types.
Generate anonymous procedure, procedure type. Generate anonymous procedure, procedure type.
## `:`(subject, T)
Determine if subject matches T. If they don't match, throw a compile error.
```python
a: Int
f x: Int, y: Int = x / y
```
Also used for `:` applied styles.
```python
fx:
y
z
```
Like `:` and `=`, the result of the operation is undefined.
```python
_ = x: Int # SyntaxError:
print!(x: Int) # SyntaxError:
```
## `.`(obj, attr) ## `.`(obj, attr)
Read attributes of obj. Read attributes of obj.
@ -87,6 +63,23 @@ Execute `c(obj)`. `x + y |>.foo()` is the same as `(x + y).foo()`.
Postfix operator. Call `x.unwrap()` and `return` immediately in case of error. Postfix operator. Call `x.unwrap()` and `return` immediately in case of error.
## `:`(x, T)
Declares that object `x` is of type `T`. An error is raised if `x` is not a subtype of `T`.
It can be used for variable declarations or as the right-hand side value of an expression.
```erg
# both are OK
x: Int = 1
y = x: Int
```
## `as`(x, T)
Forces the object `x` to be cast to type `T`. If `x` is not a subtype of `T`, an error is raised.
The difference from `:` is that `(x: T): U` when `x: U; U <: T`, but `(x as T): T`.
## match(obj, *lambdas: Lambda) ## match(obj, *lambdas: Lambda)
For obj, execute lambdas that match the pattern. For obj, execute lambdas that match the pattern.

View file

@ -2,7 +2,6 @@
[![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/API/special.md%26commit_hash%3D8673a0ce564fd282d0ca586642fa7f002e8a3c50)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/API/special.md&commit_hash=8673a0ce564fd282d0ca586642fa7f002e8a3c50) [![badge](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Fgezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com%2Fdefault%2Fsource_up_to_date%3Fowner%3Derg-lang%26repos%3Derg%26ref%3Dmain%26path%3Ddoc/EN/API/special.md%26commit_hash%3D8673a0ce564fd282d0ca586642fa7f002e8a3c50)](https://gezf7g7pd5.execute-api.ap-northeast-1.amazonaws.com/default/source_up_to_date?owner=erg-lang&repos=erg&ref=main&path=doc/EN/API/special.md&commit_hash=8673a0ce564fd282d0ca586642fa7f002e8a3c50)
特殊形式は、Ergの型システムでは表現ができない演算子、サブルーチン(のようなもの)である。``で囲っているが、実際は捕捉できない。 特殊形式は、Ergの型システムでは表現ができない演算子、サブルーチン(のようなもの)である。``で囲っているが、実際は捕捉できない。
また、`Pattern``Body`, `Conv`といった型が便宜上登場するが、そのような型が存在するわけではない。その意味もコンテクストによって異なる。 また、`Pattern``Body`, `Conv`といった型が便宜上登場するが、そのような型が存在するわけではない。その意味もコンテクストによって異なる。
@ -65,6 +64,15 @@ objの属性を読み込む。
後置演算子。`x.unwrap()`を呼び出し、エラーの場合はその場で`return`する。 後置演算子。`x.unwrap()`を呼び出し、エラーの場合はその場で`return`する。
## `:`(x, T)
オブジェクト`x``T`型であることを宣言する。`x`の型が`T`の部分型でなければエラーとなる。
## `as`(x, T)
オブジェクト`x``T`型に強制キャストする。`x`の型が`T`の部分型でなければエラーとなる。
`:`との違いとして、`x: U; U <: T`であるとき`(x: T): U`となるが、`(x as T): T`である。
## match(obj, *arms: Lambda) ## match(obj, *arms: Lambda)
objについて、パターンにマッチしたarmを実行する。armは無名関数でなくてはならない。 objについて、パターンにマッチしたarmを実行する。armは無名関数でなくてはならない。

View file

@ -5,3 +5,13 @@ n = 1
_ = n.times! _ = n.times!
i = n as Int i = n as Int
_ = i.times! # ERR _ = i.times! # ERR
v: Array!(Int or Str, 2) = ![1, 2]
v.push! 1 # OK
v.push! "a" # ERR
v.push! None # ERR
v2 as Array!(Int or Str, 2) = ![1, 2]
v2.push! 1 # OK
v2.push! "a" # OK
v2.push! None # ERR

View file

@ -244,7 +244,7 @@ fn exec_array_err() -> Result<(), ()> {
#[test] #[test]
fn exec_as() -> Result<(), ()> { fn exec_as() -> Result<(), ()> {
expect_failure("tests/should_err/as.er", 0, 3) expect_failure("tests/should_err/as.er", 0, 6)
} }
#[test] #[test]