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());
if self.opt_features.contains(&OptionalFeatures::CheckOnType) {
let uri = NormalizedUrl::new(params.text_document.uri);
// TODO: reset mutable dependent types
self.quick_check_file(uri)?;
}
Ok(())

View file

@ -1464,11 +1464,6 @@ impl PyCodeGenerator {
self.emit_push_null();
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

View file

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

View file

@ -618,10 +618,6 @@ impl Context {
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_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 */
// TODO: +/- Bool would like to be warned
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_ABS: &str = "__abs__";
const OP_PARTIAL_CMP: &str = "__partial_cmp__";
const OP_AS: &str = "__as__";
const OP_AND: &str = "__and__";
const OP_OR: &str = "__or__";
const OP_POW: &str = "__pow__";
@ -406,8 +405,6 @@ const TY_L: &str = "L";
const TY_N: &str = "N";
const TY_M: &str = "M";
const TY_O: &str = "O";
const TY_SUB: &str = "Sub";
const TY_SUP: &str = "Sup";
const KW_OLD: &str = "old";
const KW_B: &str = "b";

View file

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

View file

@ -689,13 +689,15 @@ impl Context {
let (l, r) = union.union_pair().unwrap_or((lsub, rsub));
let unified = self.unify(&l, &r);
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(
self.cfg.input.clone(),
line!() as usize,
loc.loc(),
self.caused_by(),
maybe_sub,
maybe_sup,
&maybe_sub,
&union,
)));
}
}
@ -800,14 +802,16 @@ impl Context {
let (l, r) = new_sub.union_pair().unwrap_or((maybe_sub.clone(), sub));
let unified = self.unify(&l, &r);
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(
TyCheckError::implicit_widening_error(
self.cfg.input.clone(),
line!() as usize,
loc.loc(),
self.caused_by(),
maybe_sub,
maybe_sup,
&maybe_sub,
&new_sub,
),
));
}

View file

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

View file

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

View file

@ -13,7 +13,7 @@ use erg_common::{
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::{
fmt_lines, DefId, DefKind, OperationKind, TypeBoundSpecs, TypeSpec, VarName,
};
@ -1547,7 +1547,7 @@ impl Locational for Dummy {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct VarSignature {
pub ident: Identifier,
pub t_spec: Option<TypeSpec>,
pub t_spec: Option<TypeSpecWithOp>,
}
impl NestedDisplay for VarSignature {
@ -1579,7 +1579,7 @@ impl HasType for 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 }
}
@ -2006,7 +2006,7 @@ impl Signature {
pub fn t_spec(&self) -> Option<&TypeSpec> {
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(),
}
}
@ -2352,30 +2352,39 @@ impl ReDef {
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TypeSpecWithOp {
pub op: Token,
pub t_spec: TypeSpec,
pub t_spec_as_expr: Box<Expr>,
pub raw: ast::TypeSpecWithOp,
/// Required for dynamic type checking
pub expr: Box<Expr>,
pub spec_t: Type,
}
impl NestedDisplay for TypeSpecWithOp {
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_locational!(TypeSpecWithOp, lossy op, t_spec);
impl_locational!(TypeSpecWithOp, raw);
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 {
op,
t_spec,
t_spec_as_expr: Box::new(t_spec_as_expr),
raw,
expr: Box::new(expr),
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)]
@ -2402,11 +2411,19 @@ impl_locational!(TypeAscription, expr, spec);
impl HasType for TypeAscription {
#[inline]
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]
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]
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::{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::build_ast::ASTBuilder;
use erg_parser::token::{Token, TokenKind};
@ -1097,13 +1097,8 @@ impl ASTLowerer {
type_spec_with_op: ast::TypeSpecWithOp,
spec_t: Type,
) -> LowerResult<hir::TypeSpecWithOp> {
let expr = self.fake_lower_expr(*type_spec_with_op.t_spec_as_expr)?;
Ok(hir::TypeSpecWithOp::new(
type_spec_with_op.op,
type_spec_with_op.t_spec,
expr,
spec_t,
))
let expr = self.fake_lower_expr(*type_spec_with_op.t_spec_as_expr.clone())?;
Ok(hir::TypeSpecWithOp::new(type_spec_with_op, expr, spec_t))
}
fn lower_params(&mut self, params: ast::Params) -> LowerResult<hir::Params> {
@ -1419,7 +1414,7 @@ impl ASTLowerer {
.module
.context
.instantiate_var_sig_t(
sig.t_spec.as_ref(),
sig.t_spec.as_ref().map(|ts| &ts.t_spec),
RegistrationMode::PreRegister,
)
.ok();
@ -1453,7 +1448,14 @@ impl ASTLowerer {
None,
)?;
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);
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 {
// TODO: check `tasc.op`
ast::Expr::TypeAscription(tasc) => (
self.module.context.instantiate_typespec(
self.module.context.instantiate_typespec_full(
&tasc.t_spec.t_spec,
None,
&mut dummy_tv_cache,
@ -1724,7 +1726,7 @@ impl ASTLowerer {
}
};
Ok((
self.module.context.instantiate_typespec(
self.module.context.instantiate_typespec_full(
spec,
None,
&mut dummy_tv_cache,
@ -1735,7 +1737,7 @@ impl ASTLowerer {
))
}
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,
None,
&mut dummy_tv_cache,
@ -1743,7 +1745,7 @@ impl ASTLowerer {
false,
)?;
Ok((
self.module.context.instantiate_typespec(
self.module.context.instantiate_typespec_full(
spec,
None,
&mut dummy_tv_cache,
@ -1756,7 +1758,7 @@ impl ASTLowerer {
}
}
other => Ok((
self.module.context.instantiate_typespec(
self.module.context.instantiate_typespec_full(
other,
None,
&mut dummy_tv_cache,
@ -1776,15 +1778,7 @@ impl ASTLowerer {
};
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 mut dummy_tv_cache =
TyVarCache::new(self.module.context.level, &self.module.context);
self.module.context.instantiate_typespec(
&spec,
None,
&mut dummy_tv_cache,
RegistrationMode::Normal,
false,
)?
self.module.context.instantiate_typespec(&spec)?
};
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();
@ -2168,44 +2162,43 @@ impl ASTLowerer {
fn lower_type_asc(&mut self, tasc: ast::TypeAscription) -> LowerResult<hir::TypeAscription> {
log!(info "entered {}({tasc})", fn_name!());
let is_instance_ascription = tasc.is_instance_ascription();
let mut dummy_tv_cache = TyVarCache::new(self.module.context.level, &self.module.context);
let spec_t = self.module.context.instantiate_typespec(
&tasc.t_spec.t_spec,
None,
&mut dummy_tv_cache,
RegistrationMode::Normal,
false,
)?;
let kind = tasc.kind();
let spec_t = self
.module
.context
.instantiate_typespec(&tasc.t_spec.t_spec)?;
let expr = self.lower_expr(*tasc.expr)?;
if is_instance_ascription {
self.module.context.sub_unify(
expr.ref_t(),
&spec_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:
match kind {
AscriptionKind::TypeOf | AscriptionKind::AsCast => {
self.module.context.sub_unify(
expr.ref_t(),
&spec_t,
Location::concat(&expr, &tasc.t_spec),
self.module.context.caused_by(),
)));
&expr,
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)?;
Ok(expr.type_asc(t_spec))
@ -2213,15 +2206,11 @@ impl ASTLowerer {
fn lower_decl(&mut self, tasc: ast::TypeAscription) -> LowerResult<hir::TypeAscription> {
log!(info "entered {}({tasc})", fn_name!());
let is_instance_ascription = tasc.is_instance_ascription();
let mut dummy_tv_cache = TyVarCache::new(self.module.context.level, &self.module.context);
let spec_t = self.module.context.instantiate_typespec(
&tasc.t_spec.t_spec,
None,
&mut dummy_tv_cache,
RegistrationMode::Normal,
false,
)?;
let kind = tasc.kind();
let spec_t = self
.module
.context
.instantiate_typespec(&tasc.t_spec.t_spec)?;
let ast::Expr::Accessor(ast::Accessor::Ident(ident)) = *tasc.expr else {
return Err(LowerErrors::from(LowerError::syntax_error(
self.cfg.input.clone(),
@ -2270,22 +2259,28 @@ impl ASTLowerer {
similar_info,
)
})?;
if is_instance_ascription {
self.module
.context
.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,
match kind {
AscriptionKind::TypeOf | AscriptionKind::AsCast => {
self.module.context.sub_unify(
&ident_vi.t,
&spec_t,
ident.loc(),
self.module.context.caused_by(),
)));
&ident,
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
.module

View file

@ -1909,7 +1909,6 @@ impl ConstTypeAsc {
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum ConstExpr {
Lit(Literal),
Erased(Literal), // _
Accessor(ConstAccessor),
App(ConstApp),
Array(ConstArray),
@ -1924,9 +1923,9 @@ pub enum ConstExpr {
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_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 {
pub fn need_to_be_closed(&self) -> bool {
@ -1948,7 +1947,6 @@ impl ConstExpr {
Self::BinOp(binop) => Expr::BinOp(binop.downcast()),
Self::UnaryOp(unop) => Expr::UnaryOp(unop.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 op: Token,
pub t_spec: TypeSpec,
/// Required for dynamic type checking
pub t_spec_as_expr: Box<Expr>,
}
@ -2549,6 +2548,16 @@ impl TypeSpecWithOp {
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)]
@ -3095,6 +3104,7 @@ impl VarRecordPattern {
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct VarDataPackPattern {
pub class: TypeSpec,
pub class_as_expr: Box<Expr>,
pub args: VarRecordPattern,
}
@ -3107,8 +3117,12 @@ impl fmt::Display for VarDataPackPattern {
impl_locational!(VarDataPackPattern, class, args);
impl VarDataPackPattern {
pub const fn new(class: TypeSpec, args: VarRecordPattern) -> Self {
Self { class, args }
pub const fn new(class: TypeSpec, class_as_expr: Box<Expr>, args: VarRecordPattern) -> Self {
Self {
class,
class_as_expr,
args,
}
}
}
@ -3185,7 +3199,7 @@ impl VarPattern {
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct VarSignature {
pub pat: VarPattern,
pub t_spec: Option<TypeSpec>,
pub t_spec: Option<TypeSpecWithOp>,
}
impl NestedDisplay for VarSignature {
@ -3207,7 +3221,7 @@ impl Locational for 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 }
}
@ -3860,7 +3874,7 @@ impl Signature {
pub fn t_spec(&self) -> Option<&TypeSpec> {
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(),
}
}
@ -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
/// | expr '<:' type
/// | expr ':>' type
@ -3910,12 +3938,8 @@ impl TypeAscription {
}
}
pub fn is_instance_ascription(&self) -> bool {
self.t_spec.op.is(TokenKind::Colon)
}
pub fn is_subtype_ascription(&self) -> bool {
self.t_spec.op.is(TokenKind::SubtypeOf)
pub fn kind(&self) -> AscriptionKind {
self.t_spec.ascription_kind()
}
}

View file

@ -198,12 +198,12 @@ impl Parser {
pack: DataPack,
) -> ParseResult<VarDataPackPattern> {
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
.convert_record_to_record_pat(pack.args)
.map_err(|_| self.stack_dec(fn_name!()))?;
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> {
@ -243,7 +243,7 @@ impl Parser {
.map_err(|_| self.stack_dec(fn_name!()))?;
let sig = match sig {
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::Subr(subr) => {

View file

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

View file

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

View file

@ -1209,7 +1209,7 @@ impl Parser {
// type ascription
Some(op)
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)
if in_brace {
@ -1480,7 +1480,7 @@ impl Parser {
// type ascription
Some(op)
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)
if in_brace {

View file

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

View file

@ -50,30 +50,6 @@ Generate anonymous functions, function types.
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)
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.
## `:`(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)
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)
特殊形式は、Ergの型システムでは表現ができない演算子、サブルーチン(のようなもの)である。``で囲っているが、実際は捕捉できない。
また、`Pattern``Body`, `Conv`といった型が便宜上登場するが、そのような型が存在するわけではない。その意味もコンテクストによって異なる。
@ -65,6 +64,15 @@ objの属性を読み込む。
後置演算子。`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)
objについて、パターンにマッチしたarmを実行する。armは無名関数でなくてはならない。

View file

@ -5,3 +5,13 @@ n = 1
_ = n.times!
i = n as Int
_ = 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]
fn exec_as() -> Result<(), ()> {
expect_failure("tests/should_err/as.er", 0, 3)
expect_failure("tests/should_err/as.er", 0, 6)
}
#[test]