mirror of
https://github.com/erg-lang/erg.git
synced 2025-09-28 12:14:43 +00:00
feat: impl polymorphic type declaration
This commit is contained in:
parent
ea132e2345
commit
5052ebb077
10 changed files with 289 additions and 85 deletions
|
@ -496,7 +496,7 @@ impl<'a> HIRVisitor<'a> {
|
|||
Expr::Record(record) => self.get_record_info(record, token),
|
||||
Expr::Set(set) => self.get_set_info(set, token),
|
||||
Expr::Tuple(tuple) => self.get_tuple_info(tuple, token),
|
||||
Expr::TypeAsc(type_asc) => self.get_expr_info(&type_asc.expr, token),
|
||||
Expr::TypeAsc(type_asc) => self.get_tasc_info(type_asc, token),
|
||||
Expr::Dummy(dummy) => self.get_dummy_info(dummy, token),
|
||||
Expr::Compound(block) | Expr::Code(block) => self.get_block_info(block, token),
|
||||
Expr::ReDef(redef) => self.get_redef_info(redef, token),
|
||||
|
@ -702,4 +702,9 @@ impl<'a> HIRVisitor<'a> {
|
|||
// _ => None, // todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_tasc_info(&self, tasc: &TypeAscription, token: &Token) -> Option<VarInfo> {
|
||||
self.get_expr_info(&tasc.expr, token)
|
||||
.or_else(|| self.get_expr_info(&tasc.spec.expr, token))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -782,7 +782,7 @@ impl<'c, 'q, 'l, L: Locational> Dereferencer<'c, 'q, 'l, L> {
|
|||
if self.ctx.supertype_of(&sub_t, &super_t) {
|
||||
Ok(sub_t)
|
||||
} else {
|
||||
Err(TyCheckErrors::from(TyCheckError::subtyping_error(
|
||||
Err(TyCheckErrors::from(TyCheckError::invariant_error(
|
||||
self.ctx.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
&sub_t,
|
||||
|
|
|
@ -1530,13 +1530,15 @@ impl Context {
|
|||
|
||||
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,
|
||||
)
|
||||
self.instantiate_typespec_with_tv_cache(t_spec, &mut dummy_tv_cache)
|
||||
}
|
||||
|
||||
pub(crate) fn instantiate_typespec_with_tv_cache(
|
||||
&self,
|
||||
t_spec: &ast::TypeSpec,
|
||||
tv_cache: &mut TyVarCache,
|
||||
) -> TyCheckResult<Type> {
|
||||
self.instantiate_typespec_full(t_spec, None, tv_cache, RegistrationMode::Normal, false)
|
||||
}
|
||||
|
||||
pub(crate) fn instantiate_field(&self, ident: &Identifier) -> TyCheckResult<Field> {
|
||||
|
|
|
@ -182,7 +182,7 @@ impl std::ops::Mul for Variance {
|
|||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ParamSpec {
|
||||
pub(crate) name: Option<&'static str>,
|
||||
pub(crate) name: Option<Str>,
|
||||
// TODO: `:` or `<:`
|
||||
pub(crate) t: Type,
|
||||
pub is_var_params: bool,
|
||||
|
@ -191,15 +191,15 @@ pub struct ParamSpec {
|
|||
}
|
||||
|
||||
impl ParamSpec {
|
||||
pub const fn new(
|
||||
name: Option<&'static str>,
|
||||
pub fn new<S: Into<Str>>(
|
||||
name: Option<S>,
|
||||
t: Type,
|
||||
is_var_params: bool,
|
||||
default: DefaultInfo,
|
||||
loc: AbsLocation,
|
||||
) -> Self {
|
||||
Self {
|
||||
name,
|
||||
name: name.map(|s| s.into()),
|
||||
t,
|
||||
is_var_params,
|
||||
default_info: default,
|
||||
|
@ -207,8 +207,8 @@ impl ParamSpec {
|
|||
}
|
||||
}
|
||||
|
||||
pub const fn named(
|
||||
name: &'static str,
|
||||
pub fn named<S: Into<Str>>(
|
||||
name: S,
|
||||
t: Type,
|
||||
is_var_params: bool,
|
||||
default: DefaultInfo,
|
||||
|
@ -222,7 +222,7 @@ impl ParamSpec {
|
|||
)
|
||||
}
|
||||
|
||||
pub const fn named_nd(name: &'static str, t: Type) -> Self {
|
||||
pub fn named_nd<S: Into<Str>>(name: S, t: Type) -> Self {
|
||||
Self::new(
|
||||
Some(name),
|
||||
t,
|
||||
|
@ -232,7 +232,7 @@ impl ParamSpec {
|
|||
)
|
||||
}
|
||||
|
||||
pub const fn t(name: &'static str, is_var_params: bool, default: DefaultInfo) -> Self {
|
||||
pub fn t<S: Into<Str>>(name: S, is_var_params: bool, default: DefaultInfo) -> Self {
|
||||
Self::new(
|
||||
Some(name),
|
||||
Type,
|
||||
|
@ -242,7 +242,7 @@ impl ParamSpec {
|
|||
)
|
||||
}
|
||||
|
||||
pub const fn t_nd(name: &'static str) -> Self {
|
||||
pub fn t_nd<S: Into<Str>>(name: S) -> Self {
|
||||
Self::new(
|
||||
Some(name),
|
||||
Type,
|
||||
|
@ -574,18 +574,18 @@ impl Context {
|
|||
let id = DefId(get_hash(&(&name, ¶m)));
|
||||
if let Some(name) = param.name {
|
||||
let kind = VarKind::parameter(id, param.is_var_params, param.default_info);
|
||||
let muty = Mutability::from(name);
|
||||
let muty = Mutability::from(&name[..]);
|
||||
let vi = VarInfo::new(
|
||||
param.t,
|
||||
muty,
|
||||
Visibility::private(name),
|
||||
Visibility::private(&name),
|
||||
kind,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
param.loc,
|
||||
);
|
||||
params_.push((Some(VarName::new(Token::static_symbol(name))), vi));
|
||||
params_.push((Some(VarName::new(Token::symbol(&name))), vi));
|
||||
} else {
|
||||
let kind = VarKind::parameter(id, param.is_var_params, param.default_info);
|
||||
let muty = Mutability::Immutable;
|
||||
|
|
|
@ -26,7 +26,7 @@ use ast::{
|
|||
use erg_parser::ast;
|
||||
|
||||
use crate::ty::constructors::{
|
||||
free_var, func, func0, func1, proc, ref_, ref_mut, unknown_len_array_t, v_enum,
|
||||
free_var, func, func0, func1, proc, ref_, ref_mut, tp_enum, unknown_len_array_t, v_enum,
|
||||
};
|
||||
use crate::ty::free::{Constraint, HasLevel};
|
||||
use crate::ty::typaram::TyParam;
|
||||
|
@ -49,6 +49,7 @@ use RegistrationMode::*;
|
|||
|
||||
use super::instantiate::TyVarCache;
|
||||
use super::instantiate_spec::ParamKind;
|
||||
use super::ParamSpec;
|
||||
|
||||
pub fn valid_mod_name(name: &str) -> bool {
|
||||
!name.is_empty() && !name.starts_with('/') && name.trim() == name
|
||||
|
@ -1286,66 +1287,30 @@ impl Context {
|
|||
2,
|
||||
self.level,
|
||||
);
|
||||
let mut methods =
|
||||
Self::methods(None, self.cfg.clone(), self.shared.clone(), 2, self.level);
|
||||
let new_t = if let Some(base) = gen.base_or_sup() {
|
||||
match base {
|
||||
TypeObj::Builtin {
|
||||
t: Type::Record(rec),
|
||||
..
|
||||
} => {
|
||||
for (field, t) in rec.iter() {
|
||||
let varname = VarName::from_str(field.symbol.clone());
|
||||
let vi = VarInfo::instance_attr(
|
||||
field.clone(),
|
||||
t.clone(),
|
||||
self.impl_of(),
|
||||
ctx.name.clone(),
|
||||
);
|
||||
ctx.decls.insert(varname, vi);
|
||||
if ERG_MODE {
|
||||
self.gen_class_new_method(&gen, &mut ctx)?;
|
||||
}
|
||||
}
|
||||
other => {
|
||||
methods.register_fixed_auto_impl(
|
||||
"base",
|
||||
other.typ().clone(),
|
||||
Immutable,
|
||||
Visibility::BUILTIN_PRIVATE,
|
||||
None,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
func1(base.typ().clone(), gen.typ().clone())
|
||||
} else {
|
||||
func0(gen.typ().clone())
|
||||
};
|
||||
methods.register_fixed_auto_impl(
|
||||
"__new__",
|
||||
new_t.clone(),
|
||||
Immutable,
|
||||
Visibility::BUILTIN_PRIVATE,
|
||||
Some("__call__".into()),
|
||||
)?;
|
||||
// 必要なら、ユーザーが独自に上書きする
|
||||
// users can override this if necessary
|
||||
methods.register_auto_impl(
|
||||
"new",
|
||||
new_t,
|
||||
Immutable,
|
||||
Visibility::BUILTIN_PUBLIC,
|
||||
None,
|
||||
)?;
|
||||
ctx.methods_list
|
||||
.push((ClassDefType::Simple(gen.typ().clone()), methods));
|
||||
self.register_gen_mono_type(ident, gen, ctx, Const)
|
||||
} else {
|
||||
feature_error!(
|
||||
CompileErrors,
|
||||
CompileError,
|
||||
self,
|
||||
ident.loc(),
|
||||
"polymorphic class definition"
|
||||
)
|
||||
log!(err "{:?}", gen.typ().typarams());
|
||||
let params = gen
|
||||
.typ()
|
||||
.typarams()
|
||||
.into_iter()
|
||||
.map(|tp| ParamSpec::t_nd(tp.qual_name().unwrap_or(Str::ever("_"))))
|
||||
.collect();
|
||||
let mut ctx = Self::poly_class(
|
||||
gen.typ().qual_name(),
|
||||
params,
|
||||
self.cfg.clone(),
|
||||
self.shared.clone(),
|
||||
2,
|
||||
self.level,
|
||||
);
|
||||
if ERG_MODE {
|
||||
self.gen_class_new_method(&gen, &mut ctx)?;
|
||||
}
|
||||
self.register_gen_poly_type(ident, gen, ctx, Const)
|
||||
}
|
||||
}
|
||||
GenTypeObj::Subclass(_) => {
|
||||
|
@ -1560,6 +1525,54 @@ impl Context {
|
|||
}
|
||||
}
|
||||
|
||||
fn gen_class_new_method(&self, gen: &GenTypeObj, ctx: &mut Context) -> CompileResult<()> {
|
||||
let mut methods = Self::methods(None, self.cfg.clone(), self.shared.clone(), 2, self.level);
|
||||
let new_t = if let Some(base) = gen.base_or_sup() {
|
||||
match base {
|
||||
TypeObj::Builtin {
|
||||
t: Type::Record(rec),
|
||||
..
|
||||
} => {
|
||||
for (field, t) in rec.iter() {
|
||||
let varname = VarName::from_str(field.symbol.clone());
|
||||
let vi = VarInfo::instance_attr(
|
||||
field.clone(),
|
||||
t.clone(),
|
||||
self.impl_of(),
|
||||
ctx.name.clone(),
|
||||
);
|
||||
ctx.decls.insert(varname, vi);
|
||||
}
|
||||
}
|
||||
other => {
|
||||
methods.register_fixed_auto_impl(
|
||||
"base",
|
||||
other.typ().clone(),
|
||||
Immutable,
|
||||
Visibility::BUILTIN_PRIVATE,
|
||||
None,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
func1(base.typ().clone(), gen.typ().clone())
|
||||
} else {
|
||||
func0(gen.typ().clone())
|
||||
};
|
||||
methods.register_fixed_auto_impl(
|
||||
"__new__",
|
||||
new_t.clone(),
|
||||
Immutable,
|
||||
Visibility::BUILTIN_PRIVATE,
|
||||
Some("__call__".into()),
|
||||
)?;
|
||||
// 必要なら、ユーザーが独自に上書きする
|
||||
// users can override this if necessary
|
||||
methods.register_auto_impl("new", new_t, Immutable, Visibility::BUILTIN_PUBLIC, None)?;
|
||||
ctx.methods_list
|
||||
.push((ClassDefType::Simple(gen.typ().clone()), methods));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn register_type_alias(
|
||||
&mut self,
|
||||
ident: &Identifier,
|
||||
|
@ -1656,6 +1669,66 @@ impl Context {
|
|||
}
|
||||
}
|
||||
|
||||
fn register_gen_poly_type(
|
||||
&mut self,
|
||||
ident: &Identifier,
|
||||
gen: GenTypeObj,
|
||||
ctx: Self,
|
||||
muty: Mutability,
|
||||
) -> CompileResult<()> {
|
||||
let vis = self.instantiate_vis_modifier(&ident.vis)?;
|
||||
// FIXME: recursive search
|
||||
if self.poly_types.contains_key(ident.inspect()) {
|
||||
Err(CompileErrors::from(CompileError::reassign_error(
|
||||
self.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
ident.loc(),
|
||||
self.caused_by(),
|
||||
ident.inspect(),
|
||||
)))
|
||||
} else if self.rec_get_const_obj(ident.inspect()).is_some() && vis.is_private() {
|
||||
Err(CompileErrors::from(CompileError::reassign_error(
|
||||
self.cfg.input.clone(),
|
||||
line!() as usize,
|
||||
ident.loc(),
|
||||
self.caused_by(),
|
||||
ident.inspect(),
|
||||
)))
|
||||
} else {
|
||||
let t = gen.typ().clone();
|
||||
let val = ValueObj::Type(TypeObj::Generated(gen));
|
||||
let params = t
|
||||
.typarams()
|
||||
.into_iter()
|
||||
.map(|tp| {
|
||||
ParamTy::Pos(tp_enum(
|
||||
self.get_tp_t(&tp).unwrap_or(Type::Obj),
|
||||
set! { tp },
|
||||
))
|
||||
})
|
||||
.collect();
|
||||
let meta_t = func(params, None, vec![], v_enum(set! { val.clone() })).quantify();
|
||||
let name = &ident.name;
|
||||
let id = DefId(get_hash(&(&self.name, &name)));
|
||||
let vi = VarInfo::new(
|
||||
meta_t,
|
||||
muty,
|
||||
Visibility::new(vis, self.name.clone()),
|
||||
VarKind::Defined(id),
|
||||
None,
|
||||
self.impl_of(),
|
||||
None,
|
||||
self.absolutize(name.loc()),
|
||||
);
|
||||
self.index().register(&vi);
|
||||
self.decls.insert(name.clone(), vi);
|
||||
self.consts.insert(name.clone(), val);
|
||||
self.register_methods(&t, &ctx);
|
||||
self.poly_types.insert(name.clone(), (t, ctx));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn register_gen_mono_patch(
|
||||
&mut self,
|
||||
ident: &Identifier,
|
||||
|
|
|
@ -6,17 +6,18 @@ use erg_common::{enum_unwrap, fn_name, log, set, Str, Triple};
|
|||
|
||||
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::constructors::{mono, poly, ty_tp, type_q, v_enum};
|
||||
use crate::ty::free::HasLevel;
|
||||
use crate::ty::value::{GenTypeObj, TypeObj, ValueObj};
|
||||
use crate::ty::{HasType, Type, Visibility};
|
||||
|
||||
use crate::compile::AccessKind;
|
||||
use crate::error::{LowerError, LowerErrors, LowerResult};
|
||||
use crate::hir;
|
||||
use crate::hir::HIR;
|
||||
use crate::varinfo::{Mutability, VarInfo, VarKind};
|
||||
use crate::{feature_error, hir};
|
||||
|
||||
impl ASTLowerer {
|
||||
fn declare_var(
|
||||
|
@ -576,6 +577,48 @@ impl ASTLowerer {
|
|||
let t_spec = hir::TypeSpecWithOp::new(tasc.t_spec, t_spec_expr, Type::Failure);
|
||||
Ok(attr.type_asc(t_spec))
|
||||
}
|
||||
ast::Expr::Call(call) => {
|
||||
let ast::Expr::Accessor(ast::Accessor::Ident(ident)) = *call.obj else {
|
||||
return feature_error!(
|
||||
LowerErrors,
|
||||
LowerError,
|
||||
&self.module.context,
|
||||
call.obj.loc(),
|
||||
"complex polymorphic type declaration"
|
||||
);
|
||||
};
|
||||
let py_name = Str::rc(ident.inspect().trim_end_matches('!'));
|
||||
let mut tv_cache = TyVarCache::new(self.module.context.level, &self.module.context);
|
||||
if let Some((t, _)) = self.module.context.get_type(ident.inspect()) {
|
||||
for (tp, arg) in t.typarams().iter().zip(call.args.pos_args()) {
|
||||
if let ast::Expr::Accessor(ast::Accessor::Ident(ident)) = &arg.expr {
|
||||
tv_cache.push_or_init_typaram(&ident.name, tp, &self.module.context);
|
||||
}
|
||||
}
|
||||
}
|
||||
let t = self
|
||||
.module
|
||||
.context
|
||||
.instantiate_typespec_with_tv_cache(&tasc.t_spec.t_spec, &mut tv_cache)?;
|
||||
t.lift();
|
||||
let t = self.module.context.generalize_t(t);
|
||||
match kind {
|
||||
AscriptionKind::TypeOf | AscriptionKind::AsCast => {
|
||||
self.declare_instance(&ident, &t, py_name)?;
|
||||
}
|
||||
AscriptionKind::SubtypeOf => {
|
||||
self.declare_subtype(&ident, &t)?;
|
||||
}
|
||||
_ => {
|
||||
log!(err "supertype ascription is not supported yet");
|
||||
}
|
||||
}
|
||||
let acc = self.fake_lower_acc(ast::Accessor::Ident(ident))?;
|
||||
let args = self.fake_lower_args(call.args)?;
|
||||
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(acc).call_expr(args).type_asc(t_spec))
|
||||
}
|
||||
other => Err(LowerErrors::from(LowerError::declare_error(
|
||||
self.cfg().input.clone(),
|
||||
line!() as usize,
|
||||
|
@ -616,6 +659,17 @@ impl ASTLowerer {
|
|||
let t = v_enum(set! { ValueObj::builtin_trait(t) });
|
||||
(t, Some(ty_obj))
|
||||
}
|
||||
Type::Subr(subr) if subr.return_t.is_class_type() => {
|
||||
let params = subr
|
||||
.non_default_params
|
||||
.iter()
|
||||
.map(|p| ty_tp(type_q(p.name().unwrap_or(&Str::ever("_")))))
|
||||
.collect();
|
||||
let t = poly(format!("{}{ident}", self.module.context.path()), params);
|
||||
let ty_obj = GenTypeObj::class(t.clone(), None, None);
|
||||
let t = v_enum(set! { ValueObj::builtin_class(t) });
|
||||
(t, Some(ty_obj))
|
||||
}
|
||||
_ => (t.clone(), None),
|
||||
};
|
||||
self.module.context.assign_var_sig(
|
||||
|
|
|
@ -770,6 +770,53 @@ passed keyword args: {kw_args_len}"
|
|||
)
|
||||
}
|
||||
|
||||
pub fn invariant_error(
|
||||
input: Input,
|
||||
errno: usize,
|
||||
sub_t: &Type,
|
||||
sup_t: &Type,
|
||||
loc: Location,
|
||||
caused_by: String,
|
||||
) -> Self {
|
||||
let mut sub_type = StyledStrings::default();
|
||||
switch_lang!(
|
||||
"japanese" => sub_type.push_str("部分型: "),
|
||||
"simplified_chinese" => sub_type.push_str("子类型: "),
|
||||
"simplified_chinese" =>sub_type.push_str("子類型:"),
|
||||
"english" => sub_type.push_str("subtype: "),
|
||||
);
|
||||
sub_type.push_str_with_color_and_attr(format!("{sub_t}"), HINT, ATTR);
|
||||
|
||||
let mut sup_type = StyledStrings::default();
|
||||
switch_lang!(
|
||||
"japanese" => sup_type.push_str("汎化型: "),
|
||||
"simplified_chinese" => sup_type.push_str("父类型: "),
|
||||
"simplified_chinese" => sup_type.push_str("父類型: "),
|
||||
"english" =>sup_type.push_str("supertype: "),
|
||||
);
|
||||
sup_type.push_str_with_color_and_attr(format!("{sup_t}"), ERR, ATTR);
|
||||
Self::new(
|
||||
ErrorCore::new(
|
||||
vec![SubMessage::ambiguous_new(
|
||||
loc,
|
||||
vec![sub_type.to_string(), sup_type.to_string()],
|
||||
None,
|
||||
)],
|
||||
switch_lang!(
|
||||
"japanese" => "不変な型パラメータを一意に決定できません",
|
||||
"simplified_chinese" => "无法唯一确定不变型的类型参数",
|
||||
"traditional_chinese" => "無法唯一確定不變型的類型參數",
|
||||
"english" => "cannot uniquely determine the type parameter of the invariant type",
|
||||
),
|
||||
errno,
|
||||
TypeError,
|
||||
loc,
|
||||
),
|
||||
input,
|
||||
caused_by,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn pred_unification_error(
|
||||
input: Input,
|
||||
errno: usize,
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# TODO: dependent (static shaped)
|
||||
.NDArray = 'ndarray': ClassType
|
||||
.NDArray <: Num
|
||||
.NDArray = 'ndarray': (T: Type) -> ClassType
|
||||
.NDArray(T) <: Output T
|
||||
.NDArray(_) <: Num
|
||||
.NDArray.
|
||||
shape: [Nat; _]
|
||||
ndim: Nat
|
||||
|
|
|
@ -145,7 +145,11 @@ impl OwnershipChecker {
|
|||
Expr::Accessor(acc) => self.check_acc(acc, ownership, chunk),
|
||||
// TODO: referenced
|
||||
Expr::Call(call) => {
|
||||
let args_owns = call.signature_t().unwrap().args_ownership();
|
||||
let sig_t = call.signature_t().unwrap();
|
||||
if sig_t.is_failure() || sig_t.is_class_type() {
|
||||
return;
|
||||
}
|
||||
let args_owns = sig_t.args_ownership();
|
||||
let non_defaults_len = if call.is_method_call() {
|
||||
args_owns.non_defaults.len() - 1
|
||||
} else {
|
||||
|
|
|
@ -1847,6 +1847,24 @@ impl Type {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn is_failure(&self) -> bool {
|
||||
match self {
|
||||
Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_failure(),
|
||||
Self::Refinement(refine) => refine.t.is_failure(),
|
||||
Self::Failure => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_class_type(&self) -> bool {
|
||||
match self {
|
||||
Self::FreeVar(fv) if fv.is_linked() => fv.crack().is_class_type(),
|
||||
Self::Refinement(refine) => refine.t.is_class_type(),
|
||||
Self::ClassType => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_free(&self) -> Option<&FreeTyVar> {
|
||||
<&FreeTyVar>::try_from(self).ok()
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue