diff --git a/crates/els/server.rs b/crates/els/server.rs index 8e01f487..83afc4ca 100644 --- a/crates/els/server.rs +++ b/crates/els/server.rs @@ -475,6 +475,7 @@ impl Server { 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(()) diff --git a/crates/erg_compiler/codegen.rs b/crates/erg_compiler/codegen.rs index e5e43fb5..f0093b1c 100644 --- a/crates/erg_compiler/codegen.rs +++ b/crates/erg_compiler/codegen.rs @@ -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 diff --git a/crates/erg_compiler/context/compare.rs b/crates/erg_compiler/context/compare.rs index 445a70c0..9806b4d6 100644 --- a/crates/erg_compiler/context/compare.rs +++ b/crates/erg_compiler/context/compare.rs @@ -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(<, &rt); } _ => {} diff --git a/crates/erg_compiler/context/initialize/funcs.rs b/crates/erg_compiler/context/initialize/funcs.rs index 2dec4aaf..7e70eee6 100644 --- a/crates/erg_compiler/context/initialize/funcs.rs +++ b/crates/erg_compiler/context/initialize/funcs.rs @@ -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))); diff --git a/crates/erg_compiler/context/initialize/mod.rs b/crates/erg_compiler/context/initialize/mod.rs index 765582f6..aa763c64 100644 --- a/crates/erg_compiler/context/initialize/mod.rs +++ b/crates/erg_compiler/context/initialize/mod.rs @@ -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"; diff --git a/crates/erg_compiler/context/instantiate_spec.rs b/crates/erg_compiler/context/instantiate_spec.rs index c73b7c3c..3f421219 100644 --- a/crates/erg_compiler/context/instantiate_spec.rs +++ b/crates/erg_compiler/context/instantiate_spec.rs @@ -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 { 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(¶m).unwrap_or(Obj); + if self.subtype_of(&arg_t, ¶m_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), + ¶m_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 { - 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 { + pub(crate) fn instantiate_typespec(&self, t_spec: &ast::TypeSpec) -> TyCheckResult { + 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 { 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 { @@ -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 { 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 { diff --git a/crates/erg_compiler/context/register.rs b/crates/erg_compiler/context/register.rs index d86541ea..393d71a0 100644 --- a/crates/erg_compiler/context/register.rs +++ b/crates/erg_compiler/context/register.rs @@ -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, diff --git a/crates/erg_compiler/context/unify.rs b/crates/erg_compiler/context/unify.rs index faba5b56..9e07b438 100644 --- a/crates/erg_compiler/context/unify.rs +++ b/crates/erg_compiler/context/unify.rs @@ -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, ), )); } diff --git a/crates/erg_compiler/declare.rs b/crates/erg_compiler/declare.rs index 5ae980bb..16c27b66 100644 --- a/crates/erg_compiler/declare.rs +++ b/crates/erg_compiler/declare.rs @@ -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 { 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 { 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( diff --git a/crates/erg_compiler/error/lower.rs b/crates/erg_compiler/error/lower.rs index 56b9962a..c8e0fdc2 100644 --- a/crates/erg_compiler/error/lower.rs +++ b/crates/erg_compiler/error/lower.rs @@ -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, diff --git a/crates/erg_compiler/error/tycheck.rs b/crates/erg_compiler/error/tycheck.rs index b8144bf6..8014b1b0 100644 --- a/crates/erg_compiler/error/tycheck.rs +++ b/crates/erg_compiler/error/tycheck.rs @@ -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), ) diff --git a/crates/erg_compiler/hir.rs b/crates/erg_compiler/hir.rs index 3a7c4e64..4bef507a 100644 --- a/crates/erg_compiler/hir.rs +++ b/crates/erg_compiler/hir.rs @@ -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, + pub t_spec: Option, } impl NestedDisplay for VarSignature { @@ -1579,7 +1579,7 @@ impl HasType for VarSignature { } impl VarSignature { - pub const fn new(ident: Identifier, t_spec: Option) -> Self { + pub const fn new(ident: Identifier, t_spec: Option) -> 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, + pub raw: ast::TypeSpecWithOp, + /// Required for dynamic type checking + pub expr: Box, 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> { diff --git a/crates/erg_compiler/lower.rs b/crates/erg_compiler/lower.rs index 7f7db641..d09599e2 100644 --- a/crates/erg_compiler/lower.rs +++ b/crates/erg_compiler/lower.rs @@ -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 { - 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 { @@ -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 { 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 { 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 diff --git a/crates/erg_parser/ast.rs b/crates/erg_parser/ast.rs index db575d4d..97a9b7f0 100644 --- a/crates/erg_parser/ast.rs +++ b/crates/erg_parser/ast.rs @@ -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, } @@ -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, 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, 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, + pub t_spec: Option, } impl NestedDisplay for VarSignature { @@ -3207,7 +3221,7 @@ impl Locational for VarSignature { } impl VarSignature { - pub const fn new(pat: VarPattern, t_spec: Option) -> Self { + pub const fn new(pat: VarPattern, t_spec: Option) -> 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() } } diff --git a/crates/erg_parser/convert.rs b/crates/erg_parser/convert.rs index 632121d1..7ba32fda 100644 --- a/crates/erg_parser/convert.rs +++ b/crates/erg_parser/convert.rs @@ -198,12 +198,12 @@ impl Parser { pack: DataPack, ) -> ParseResult { 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 { @@ -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) => { diff --git a/crates/erg_parser/desugar.rs b/crates/erg_parser/desugar.rs index 2addae36..edd6766d 100644 --- a/crates/erg_parser/desugar.rs +++ b/crates/erg_parser/desugar.rs @@ -498,7 +498,11 @@ impl Desugarer { todo!() } - fn gen_buf_name_and_sig(&mut self, line: u32, t_spec: Option) -> (String, Signature) { + fn gen_buf_name_and_sig( + &mut self, + line: u32, + t_spec: Option, + ) -> (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; diff --git a/crates/erg_parser/lex.rs b/crates/erg_parser/lex.rs index f0007acb..c93af598 100644 --- a/crates/erg_parser/lex.rs +++ b/crates/erg_parser/lex.rs @@ -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, diff --git a/crates/erg_parser/parse.rs b/crates/erg_parser/parse.rs index ac03fae5..25a7a236 100644 --- a/crates/erg_parser/parse.rs +++ b/crates/erg_parser/parse.rs @@ -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 { diff --git a/crates/erg_parser/token.rs b/crates/erg_parser/token.rs index 40d2a889..d05e168d 100644 --- a/crates/erg_parser/token.rs +++ b/crates/erg_parser/token.rs @@ -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) diff --git a/doc/EN/API/special.md b/doc/EN/API/special.md index 5419b87d..b7385b93 100644 --- a/doc/EN/API/special.md +++ b/doc/EN/API/special.md @@ -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. diff --git a/doc/JA/API/special.md b/doc/JA/API/special.md index 2f116b3f..e7653946 100644 --- a/doc/JA/API/special.md +++ b/doc/JA/API/special.md @@ -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は無名関数でなくてはならない。 diff --git a/tests/should_err/as.er b/tests/should_err/as.er index 684f3e03..03c7ac27 100644 --- a/tests/should_err/as.er +++ b/tests/should_err/as.er @@ -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 diff --git a/tests/test.rs b/tests/test.rs index a6147e5f..ddd6cf7b 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -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]