2024-10-03 12:37:56 +08:00
|
|
|
use std::{collections::HashMap, sync::Arc};
|
|
|
|
|
|
|
|
use indoc::indoc;
|
|
|
|
use parking_lot::Mutex;
|
|
|
|
use test_case::test_case;
|
|
|
|
|
|
|
|
use nac3parser::{
|
|
|
|
ast::{fold::Fold, FileName},
|
|
|
|
parser::parse_program,
|
|
|
|
};
|
|
|
|
|
2024-07-02 11:05:05 +08:00
|
|
|
use super::*;
|
2021-08-31 15:22:45 +08:00
|
|
|
use crate::{
|
2021-10-06 16:07:42 +08:00
|
|
|
codegen::CodeGenContext,
|
2021-11-20 19:50:25 +08:00
|
|
|
symbol_resolver::{SymbolResolver, ValueEnum},
|
2024-10-03 12:37:56 +08:00
|
|
|
toplevel::{helper::PrimDef, DefinitionId},
|
2021-08-31 15:22:45 +08:00
|
|
|
typecheck::{
|
|
|
|
type_inferencer::PrimitiveStore,
|
2024-10-03 12:37:56 +08:00
|
|
|
typedef::{into_var_map, Type, Unifier},
|
2021-08-31 15:22:45 +08:00
|
|
|
},
|
|
|
|
};
|
|
|
|
|
2021-09-08 19:27:32 +08:00
|
|
|
struct ResolverInternal {
|
2021-09-22 17:19:27 +08:00
|
|
|
id_to_type: Mutex<HashMap<StrRef, Type>>,
|
|
|
|
id_to_def: Mutex<HashMap<StrRef, DefinitionId>>,
|
|
|
|
class_names: Mutex<HashMap<StrRef, Type>>,
|
2021-08-31 15:22:45 +08:00
|
|
|
}
|
|
|
|
|
2021-09-08 19:27:32 +08:00
|
|
|
impl ResolverInternal {
|
2021-09-22 17:19:27 +08:00
|
|
|
fn add_id_def(&self, id: StrRef, def: DefinitionId) {
|
2021-09-08 19:27:32 +08:00
|
|
|
self.id_to_def.lock().insert(id, def);
|
|
|
|
}
|
2021-09-14 22:49:20 +08:00
|
|
|
|
2021-09-22 17:19:27 +08:00
|
|
|
fn add_id_type(&self, id: StrRef, ty: Type) {
|
2021-09-14 22:49:20 +08:00
|
|
|
self.id_to_type.lock().insert(id, ty);
|
|
|
|
}
|
2021-09-08 19:27:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
struct Resolver(Arc<ResolverInternal>);
|
|
|
|
|
2021-08-31 15:22:45 +08:00
|
|
|
impl SymbolResolver for Resolver {
|
2022-02-21 18:27:46 +08:00
|
|
|
fn get_default_param_value(
|
|
|
|
&self,
|
2023-10-26 13:52:40 +08:00
|
|
|
_: &ast::Expr,
|
2022-02-21 18:27:46 +08:00
|
|
|
) -> Option<crate::symbol_resolver::SymbolValue> {
|
2021-11-23 07:32:09 +08:00
|
|
|
unimplemented!()
|
|
|
|
}
|
2022-02-12 21:21:56 +08:00
|
|
|
|
2021-10-16 18:08:13 +08:00
|
|
|
fn get_symbol_type(
|
|
|
|
&self,
|
|
|
|
_: &mut Unifier,
|
|
|
|
_: &[Arc<RwLock<TopLevelDef>>],
|
|
|
|
_: &PrimitiveStore,
|
|
|
|
str: StrRef,
|
2022-01-13 03:21:26 +08:00
|
|
|
) -> Result<Type, String> {
|
|
|
|
self.0
|
|
|
|
.id_to_type
|
|
|
|
.lock()
|
|
|
|
.get(&str)
|
2024-06-17 14:07:38 +08:00
|
|
|
.copied()
|
|
|
|
.ok_or_else(|| format!("cannot find symbol `{str}`"))
|
2021-08-31 15:22:45 +08:00
|
|
|
}
|
|
|
|
|
2024-06-17 14:07:38 +08:00
|
|
|
fn get_symbol_value<'ctx>(
|
2021-10-06 16:07:42 +08:00
|
|
|
&self,
|
|
|
|
_: StrRef,
|
2024-06-17 14:07:38 +08:00
|
|
|
_: &mut CodeGenContext<'ctx, '_>,
|
2024-10-04 12:51:38 +08:00
|
|
|
_: &mut dyn CodeGenerator,
|
2021-11-20 19:50:25 +08:00
|
|
|
) -> Option<ValueEnum<'ctx>> {
|
2021-08-31 15:22:45 +08:00
|
|
|
unimplemented!()
|
|
|
|
}
|
|
|
|
|
2023-11-15 17:30:26 +08:00
|
|
|
fn get_identifier_def(&self, id: StrRef) -> Result<DefinitionId, HashSet<String>> {
|
2024-06-12 14:45:03 +08:00
|
|
|
self.0
|
|
|
|
.id_to_def
|
|
|
|
.lock()
|
|
|
|
.get(&id)
|
2024-06-17 14:07:38 +08:00
|
|
|
.copied()
|
2023-11-15 17:30:26 +08:00
|
|
|
.ok_or_else(|| HashSet::from(["Unknown identifier".to_string()]))
|
2021-08-31 15:22:45 +08:00
|
|
|
}
|
2022-02-12 21:21:56 +08:00
|
|
|
|
|
|
|
fn get_string_id(&self, _: &str) -> i32 {
|
|
|
|
unimplemented!()
|
|
|
|
}
|
2022-03-05 00:27:51 +08:00
|
|
|
|
2022-03-26 18:52:08 +08:00
|
|
|
fn get_exception_id(&self, _tyid: usize) -> usize {
|
2022-03-05 00:27:51 +08:00
|
|
|
unimplemented!()
|
|
|
|
}
|
2021-08-31 15:22:45 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test_case(
|
|
|
|
vec![
|
|
|
|
indoc! {"
|
2021-09-07 00:20:40 +08:00
|
|
|
def fun(a: int32) -> int32:
|
2021-08-31 15:22:45 +08:00
|
|
|
return a
|
|
|
|
"},
|
|
|
|
indoc! {"
|
|
|
|
class A:
|
|
|
|
def __init__(self):
|
2021-09-07 00:20:40 +08:00
|
|
|
self.a: int32 = 3
|
2021-08-31 15:22:45 +08:00
|
|
|
"},
|
|
|
|
indoc! {"
|
|
|
|
class B:
|
|
|
|
def __init__(self):
|
|
|
|
self.b: float = 4.3
|
2021-09-22 17:19:27 +08:00
|
|
|
|
2021-08-31 15:22:45 +08:00
|
|
|
def fun(self):
|
|
|
|
self.b = self.b + 3.0
|
2021-08-31 15:41:48 +08:00
|
|
|
"},
|
|
|
|
indoc! {"
|
|
|
|
def foo(a: float):
|
|
|
|
a + 1.0
|
2021-08-31 17:40:38 +08:00
|
|
|
"},
|
|
|
|
indoc! {"
|
|
|
|
class C(B):
|
|
|
|
def __init__(self):
|
2021-09-07 00:20:40 +08:00
|
|
|
self.c: int32 = 4
|
2021-08-31 17:40:38 +08:00
|
|
|
self.a: bool = True
|
2022-07-07 10:36:25 +08:00
|
|
|
"},
|
2021-09-08 19:27:32 +08:00
|
|
|
];
|
|
|
|
"register"
|
2021-08-31 15:22:45 +08:00
|
|
|
)]
|
|
|
|
fn test_simple_register(source: Vec<&str>) {
|
2024-08-12 18:00:23 +08:00
|
|
|
let mut composer =
|
|
|
|
TopLevelComposer::new(Vec::new(), Vec::new(), ComposerConfig::default(), 64).0;
|
2021-08-31 15:22:45 +08:00
|
|
|
|
|
|
|
for s in source {
|
2024-06-17 14:07:38 +08:00
|
|
|
let ast = parse_program(s, FileName::default()).unwrap();
|
2021-08-31 15:22:45 +08:00
|
|
|
let ast = ast[0].clone();
|
|
|
|
|
2024-06-17 14:07:38 +08:00
|
|
|
composer.register_top_level(ast, None, "", false).unwrap();
|
2021-08-31 15:22:45 +08:00
|
|
|
}
|
|
|
|
}
|
2021-09-06 19:23:04 +08:00
|
|
|
|
2022-07-07 10:36:25 +08:00
|
|
|
#[test_case(
|
|
|
|
indoc! {"
|
|
|
|
class A:
|
|
|
|
def foo(self):
|
|
|
|
pass
|
|
|
|
a = A()
|
|
|
|
"};
|
|
|
|
"register"
|
|
|
|
)]
|
|
|
|
fn test_simple_register_without_constructor(source: &str) {
|
2024-08-12 18:00:23 +08:00
|
|
|
let mut composer =
|
|
|
|
TopLevelComposer::new(Vec::new(), Vec::new(), ComposerConfig::default(), 64).0;
|
2024-06-17 14:07:38 +08:00
|
|
|
let ast = parse_program(source, FileName::default()).unwrap();
|
2022-07-07 10:36:25 +08:00
|
|
|
let ast = ast[0].clone();
|
2024-06-17 14:07:38 +08:00
|
|
|
composer.register_top_level(ast, None, "", true).unwrap();
|
2022-07-07 10:36:25 +08:00
|
|
|
}
|
|
|
|
|
2021-09-06 19:23:04 +08:00
|
|
|
#[test_case(
|
2024-06-17 14:07:38 +08:00
|
|
|
&[
|
2021-09-06 19:23:04 +08:00
|
|
|
indoc! {"
|
2021-09-07 00:20:40 +08:00
|
|
|
def fun(a: int32) -> int32:
|
2021-09-06 19:23:04 +08:00
|
|
|
return a
|
|
|
|
"},
|
2021-09-07 00:20:40 +08:00
|
|
|
indoc! {"
|
|
|
|
def foo(a: float):
|
|
|
|
a + 1.0
|
|
|
|
"},
|
|
|
|
indoc! {"
|
|
|
|
def f(b: int64) -> int32:
|
|
|
|
return 3
|
|
|
|
"},
|
|
|
|
],
|
2024-06-17 14:07:38 +08:00
|
|
|
&[
|
2022-02-21 17:52:34 +08:00
|
|
|
"fn[[a:0], 0]",
|
|
|
|
"fn[[a:2], 4]",
|
|
|
|
"fn[[b:1], 0]",
|
2021-09-07 00:20:40 +08:00
|
|
|
],
|
2024-06-17 14:07:38 +08:00
|
|
|
&[
|
2021-09-07 00:20:40 +08:00
|
|
|
"fun",
|
|
|
|
"foo",
|
|
|
|
"f"
|
2021-09-08 19:27:32 +08:00
|
|
|
];
|
|
|
|
"function compose"
|
2021-09-06 19:23:04 +08:00
|
|
|
)]
|
2024-06-17 14:07:38 +08:00
|
|
|
fn test_simple_function_analyze(source: &[&str], tys: &[&str], names: &[&str]) {
|
2024-08-12 18:00:23 +08:00
|
|
|
let mut composer =
|
|
|
|
TopLevelComposer::new(Vec::new(), Vec::new(), ComposerConfig::default(), 64).0;
|
2021-09-08 02:27:12 +08:00
|
|
|
|
2021-09-08 19:27:32 +08:00
|
|
|
let internal_resolver = Arc::new(ResolverInternal {
|
2024-06-17 14:07:38 +08:00
|
|
|
id_to_def: Mutex::default(),
|
|
|
|
id_to_type: Mutex::default(),
|
|
|
|
class_names: Mutex::default(),
|
2021-09-08 19:27:32 +08:00
|
|
|
});
|
2021-10-16 18:08:13 +08:00
|
|
|
let resolver =
|
|
|
|
Arc::new(Resolver(internal_resolver.clone())) as Arc<dyn SymbolResolver + Send + Sync>;
|
2021-09-06 19:23:04 +08:00
|
|
|
|
|
|
|
for s in source {
|
2024-06-17 14:07:38 +08:00
|
|
|
let ast = parse_program(s, FileName::default()).unwrap();
|
2021-09-06 19:23:04 +08:00
|
|
|
let ast = ast[0].clone();
|
|
|
|
|
2021-09-14 22:49:20 +08:00
|
|
|
let (id, def_id, ty) =
|
2024-06-17 14:07:38 +08:00
|
|
|
composer.register_top_level(ast, Some(resolver.clone()), "", false).unwrap();
|
2021-10-16 18:08:13 +08:00
|
|
|
internal_resolver.add_id_def(id, def_id);
|
2021-09-14 22:49:20 +08:00
|
|
|
if let Some(ty) = ty {
|
|
|
|
internal_resolver.add_id_type(id, ty);
|
|
|
|
}
|
2021-09-06 19:23:04 +08:00
|
|
|
}
|
|
|
|
|
2021-09-14 16:16:48 +08:00
|
|
|
composer.start_analysis(true).unwrap();
|
2021-09-08 02:27:12 +08:00
|
|
|
|
2021-12-02 10:45:46 +08:00
|
|
|
for (i, (def, _)) in composer.definition_ast_list.iter().skip(composer.builtin_num).enumerate()
|
2021-09-20 14:24:16 +08:00
|
|
|
{
|
2021-09-07 00:20:40 +08:00
|
|
|
let def = &*def.read();
|
|
|
|
if let TopLevelDef::Function { signature, name, .. } = def {
|
2022-02-21 18:27:46 +08:00
|
|
|
let ty_str = composer.unifier.internal_stringify(
|
|
|
|
*signature,
|
|
|
|
&mut |id| id.to_string(),
|
|
|
|
&mut |id| id.to_string(),
|
|
|
|
&mut None,
|
|
|
|
);
|
2021-09-07 00:20:40 +08:00
|
|
|
assert_eq!(ty_str, tys[i]);
|
|
|
|
assert_eq!(name, names[i]);
|
|
|
|
}
|
|
|
|
}
|
2021-09-06 19:23:04 +08:00
|
|
|
}
|
2021-09-07 10:03:31 +08:00
|
|
|
|
|
|
|
#[test_case(
|
2024-06-17 14:07:38 +08:00
|
|
|
&[
|
2021-09-07 10:03:31 +08:00
|
|
|
indoc! {"
|
2021-09-07 17:30:15 +08:00
|
|
|
class A():
|
2021-09-12 03:49:21 +08:00
|
|
|
a: int32
|
2021-09-10 16:14:08 +08:00
|
|
|
def __init__(self):
|
2021-09-12 03:49:21 +08:00
|
|
|
self.a = 3
|
2021-09-12 03:01:56 +08:00
|
|
|
def fun(self, b: B):
|
2021-09-07 10:03:31 +08:00
|
|
|
pass
|
2021-09-12 03:01:56 +08:00
|
|
|
def foo(self, a: T, b: V):
|
2021-09-08 02:27:12 +08:00
|
|
|
pass
|
2021-09-07 10:03:31 +08:00
|
|
|
"},
|
|
|
|
indoc! {"
|
|
|
|
class B(C):
|
2021-09-10 16:14:08 +08:00
|
|
|
def __init__(self):
|
2021-09-07 10:03:31 +08:00
|
|
|
pass
|
|
|
|
"},
|
|
|
|
indoc! {"
|
|
|
|
class C(A):
|
2021-09-10 16:14:08 +08:00
|
|
|
def __init__(self):
|
2021-09-07 17:30:15 +08:00
|
|
|
pass
|
2021-09-12 03:01:56 +08:00
|
|
|
def fun(self, b: B):
|
2021-09-07 17:30:15 +08:00
|
|
|
a = 1
|
2021-09-07 10:03:31 +08:00
|
|
|
pass
|
|
|
|
"},
|
|
|
|
indoc! {"
|
|
|
|
def foo(a: A):
|
|
|
|
pass
|
|
|
|
"},
|
2021-09-08 02:27:12 +08:00
|
|
|
indoc! {"
|
|
|
|
def ff(a: T) -> V:
|
|
|
|
pass
|
|
|
|
"}
|
2021-09-07 17:30:15 +08:00
|
|
|
],
|
2024-06-17 14:07:38 +08:00
|
|
|
&[];
|
2021-09-08 19:27:32 +08:00
|
|
|
"simple class compose"
|
|
|
|
)]
|
|
|
|
#[test_case(
|
2024-06-17 14:07:38 +08:00
|
|
|
&[
|
2021-09-08 19:27:32 +08:00
|
|
|
indoc! {"
|
2021-09-08 21:53:54 +08:00
|
|
|
class Generic_A(Generic[V], B):
|
2021-09-12 03:49:21 +08:00
|
|
|
a: int64
|
2021-09-10 16:14:08 +08:00
|
|
|
def __init__(self):
|
2021-09-12 03:49:21 +08:00
|
|
|
self.a = 123123123123
|
2021-09-10 16:14:08 +08:00
|
|
|
def fun(self, a: int32) -> V:
|
2021-09-08 21:53:54 +08:00
|
|
|
pass
|
|
|
|
"},
|
|
|
|
indoc! {"
|
|
|
|
class B:
|
2021-09-12 03:49:21 +08:00
|
|
|
aa: bool
|
2021-09-10 16:14:08 +08:00
|
|
|
def __init__(self):
|
2021-09-12 03:49:21 +08:00
|
|
|
self.aa = False
|
2021-09-10 16:14:08 +08:00
|
|
|
def foo(self, b: T):
|
2021-09-08 19:27:32 +08:00
|
|
|
pass
|
|
|
|
"}
|
|
|
|
],
|
2024-06-17 14:07:38 +08:00
|
|
|
&[];
|
2021-09-08 19:27:32 +08:00
|
|
|
"generic class"
|
2021-09-07 10:03:31 +08:00
|
|
|
)]
|
2021-09-09 02:03:44 +08:00
|
|
|
#[test_case(
|
2024-06-17 14:07:38 +08:00
|
|
|
&[
|
2021-09-09 02:03:44 +08:00
|
|
|
indoc! {"
|
|
|
|
def foo(a: list[int32], b: tuple[T, float]) -> A[B, bool]:
|
|
|
|
pass
|
|
|
|
"},
|
|
|
|
indoc! {"
|
|
|
|
class A(Generic[T, V]):
|
2021-09-12 03:49:21 +08:00
|
|
|
a: T
|
|
|
|
b: V
|
2021-09-10 16:14:08 +08:00
|
|
|
def __init__(self, v: V):
|
2021-09-12 03:49:21 +08:00
|
|
|
self.a = 1
|
|
|
|
self.b = v
|
2021-09-12 03:01:56 +08:00
|
|
|
def fun(self, a: T) -> V:
|
2021-09-09 02:03:44 +08:00
|
|
|
pass
|
|
|
|
"},
|
|
|
|
indoc! {"
|
|
|
|
def gfun(a: A[list[float], int32]):
|
|
|
|
pass
|
|
|
|
"},
|
|
|
|
indoc! {"
|
|
|
|
class B:
|
2021-09-10 16:14:08 +08:00
|
|
|
def __init__(self):
|
2021-09-09 02:03:44 +08:00
|
|
|
pass
|
|
|
|
"}
|
|
|
|
],
|
2024-06-17 14:07:38 +08:00
|
|
|
&[];
|
2021-09-09 02:03:44 +08:00
|
|
|
"list tuple generic"
|
|
|
|
)]
|
2021-09-12 03:01:56 +08:00
|
|
|
#[test_case(
|
2024-06-17 14:07:38 +08:00
|
|
|
&[
|
2021-09-12 03:01:56 +08:00
|
|
|
indoc! {"
|
|
|
|
class A(Generic[T, V]):
|
2021-09-12 03:49:21 +08:00
|
|
|
a: A[float, bool]
|
|
|
|
b: B
|
2021-09-12 03:01:56 +08:00
|
|
|
def __init__(self, a: A[float, bool], b: B):
|
2021-09-12 03:49:21 +08:00
|
|
|
self.a = a
|
|
|
|
self.b = b
|
2021-09-12 03:01:56 +08:00
|
|
|
def fun(self, a: A[float, bool]) -> A[bool, int32]:
|
|
|
|
pass
|
|
|
|
"},
|
|
|
|
indoc! {"
|
|
|
|
class B(A[int64, bool]):
|
|
|
|
def __init__(self):
|
|
|
|
pass
|
|
|
|
def foo(self, b: B) -> B:
|
|
|
|
pass
|
|
|
|
def bar(self, a: A[list[B], int32]) -> tuple[A[virtual[A[B, int32]], bool], B]:
|
|
|
|
pass
|
|
|
|
"}
|
|
|
|
],
|
2024-06-17 14:07:38 +08:00
|
|
|
&[];
|
2021-09-12 03:01:56 +08:00
|
|
|
"self1"
|
|
|
|
)]
|
2021-09-12 04:34:30 +08:00
|
|
|
#[test_case(
|
2024-06-17 14:07:38 +08:00
|
|
|
&[
|
2021-09-12 04:34:30 +08:00
|
|
|
indoc! {"
|
|
|
|
class A(Generic[T]):
|
|
|
|
a: int32
|
|
|
|
b: T
|
|
|
|
c: A[int64]
|
|
|
|
def __init__(self, t: T):
|
|
|
|
self.a = 3
|
|
|
|
self.b = T
|
|
|
|
def fun(self, a: int32, b: T) -> list[virtual[B[bool]]]:
|
|
|
|
pass
|
|
|
|
def foo(self, c: C):
|
|
|
|
pass
|
|
|
|
"},
|
|
|
|
indoc! {"
|
|
|
|
class B(Generic[V], A[float]):
|
|
|
|
d: C
|
|
|
|
def __init__(self):
|
|
|
|
pass
|
|
|
|
def fun(self, a: int32, b: T) -> list[virtual[B[bool]]]:
|
|
|
|
# override
|
|
|
|
pass
|
|
|
|
"},
|
|
|
|
indoc! {"
|
|
|
|
class C(B[bool]):
|
|
|
|
e: int64
|
|
|
|
def __init__(self):
|
|
|
|
pass
|
|
|
|
"}
|
|
|
|
],
|
2024-06-17 14:07:38 +08:00
|
|
|
&[];
|
2021-09-12 04:34:30 +08:00
|
|
|
"inheritance_override"
|
|
|
|
)]
|
2021-09-12 03:01:56 +08:00
|
|
|
#[test_case(
|
2024-06-17 14:07:38 +08:00
|
|
|
&[
|
2021-09-12 03:01:56 +08:00
|
|
|
indoc! {"
|
|
|
|
class A(Generic[T]):
|
|
|
|
def __init__(self):
|
|
|
|
pass
|
|
|
|
def fun(self, a: A[T]) -> A[T]:
|
|
|
|
pass
|
|
|
|
"}
|
|
|
|
],
|
2024-06-17 14:07:38 +08:00
|
|
|
&["application of type vars to generic class is not currently supported (at unknown:4:24)"];
|
2021-09-12 03:01:56 +08:00
|
|
|
"err no type var in generic app"
|
|
|
|
)]
|
|
|
|
#[test_case(
|
2024-06-17 14:07:38 +08:00
|
|
|
&[
|
2021-09-12 03:01:56 +08:00
|
|
|
indoc! {"
|
|
|
|
class A(B):
|
|
|
|
def __init__(self):
|
|
|
|
pass
|
|
|
|
"},
|
|
|
|
indoc! {"
|
|
|
|
class B(A):
|
|
|
|
def __init__(self):
|
|
|
|
pass
|
|
|
|
"}
|
|
|
|
],
|
2024-06-17 14:07:38 +08:00
|
|
|
&["cyclic inheritance detected"];
|
2021-09-12 03:01:56 +08:00
|
|
|
"cyclic1"
|
|
|
|
)]
|
|
|
|
#[test_case(
|
2024-06-17 14:07:38 +08:00
|
|
|
&[
|
2021-09-12 03:01:56 +08:00
|
|
|
indoc! {"
|
|
|
|
class A(B[bool, int64]):
|
|
|
|
def __init__(self):
|
|
|
|
pass
|
|
|
|
"},
|
|
|
|
indoc! {"
|
|
|
|
class B(Generic[V, T], C[int32]):
|
|
|
|
def __init__(self):
|
|
|
|
pass
|
|
|
|
"},
|
|
|
|
indoc! {"
|
|
|
|
class C(Generic[T], A):
|
|
|
|
def __init__(self):
|
|
|
|
pass
|
|
|
|
"},
|
|
|
|
],
|
2024-06-17 14:07:38 +08:00
|
|
|
&["cyclic inheritance detected"];
|
2021-09-12 03:01:56 +08:00
|
|
|
"cyclic2"
|
|
|
|
)]
|
2021-09-20 14:24:16 +08:00
|
|
|
#[test_case(
|
2024-06-17 14:07:38 +08:00
|
|
|
&[
|
2021-09-20 14:24:16 +08:00
|
|
|
indoc! {"
|
|
|
|
class A:
|
|
|
|
pass
|
|
|
|
"}
|
|
|
|
],
|
2024-06-17 14:07:38 +08:00
|
|
|
&["5: Class {\nname: \"A\",\ndef_id: DefinitionId(5),\nancestors: [CustomClassKind { id: DefinitionId(5), params: [] }],\nfields: [],\nmethods: [],\ntype_vars: []\n}"];
|
2021-09-20 14:24:16 +08:00
|
|
|
"simple pass in class"
|
|
|
|
)]
|
2021-09-10 21:26:39 +08:00
|
|
|
#[test_case(
|
2024-06-17 14:07:38 +08:00
|
|
|
&[indoc! {"
|
2021-09-10 21:26:39 +08:00
|
|
|
class A:
|
|
|
|
def __init__():
|
|
|
|
pass
|
|
|
|
"}],
|
2024-06-17 14:07:38 +08:00
|
|
|
&["__init__ method must have a `self` parameter (at unknown:2:5)"];
|
2021-09-10 21:26:39 +08:00
|
|
|
"err no self_1"
|
|
|
|
)]
|
|
|
|
#[test_case(
|
2024-06-17 14:07:38 +08:00
|
|
|
&[
|
2021-09-10 21:26:39 +08:00
|
|
|
indoc! {"
|
|
|
|
class A(B, Generic[T], C):
|
|
|
|
def __init__(self):
|
|
|
|
pass
|
|
|
|
"},
|
|
|
|
indoc! {"
|
|
|
|
class B:
|
|
|
|
def __init__(self):
|
|
|
|
pass
|
|
|
|
"},
|
|
|
|
indoc! {"
|
|
|
|
class C:
|
|
|
|
def __init__(self):
|
|
|
|
pass
|
|
|
|
"}
|
|
|
|
|
|
|
|
],
|
2024-06-17 14:07:38 +08:00
|
|
|
&["a class definition can only have at most one base class declaration and one generic declaration (at unknown:1:24)"];
|
2021-09-10 21:26:39 +08:00
|
|
|
"err multiple inheritance"
|
|
|
|
)]
|
2021-09-12 04:34:30 +08:00
|
|
|
#[test_case(
|
2024-06-17 14:07:38 +08:00
|
|
|
&[
|
2021-09-12 04:34:30 +08:00
|
|
|
indoc! {"
|
|
|
|
class A(Generic[T]):
|
|
|
|
a: int32
|
|
|
|
b: T
|
|
|
|
c: A[int64]
|
|
|
|
def __init__(self, t: T):
|
|
|
|
self.a = 3
|
|
|
|
self.b = T
|
|
|
|
def fun(self, a: int32, b: T) -> list[virtual[B[bool]]]:
|
|
|
|
pass
|
|
|
|
"},
|
|
|
|
indoc! {"
|
|
|
|
class B(Generic[V], A[float]):
|
|
|
|
def __init__(self):
|
|
|
|
pass
|
|
|
|
def fun(self, a: int32, b: T) -> list[virtual[B[int32]]]:
|
|
|
|
# override
|
|
|
|
pass
|
|
|
|
"}
|
|
|
|
],
|
2024-06-17 14:07:38 +08:00
|
|
|
&["method fun has same name as ancestors' method, but incompatible type"];
|
2021-09-12 04:34:30 +08:00
|
|
|
"err_incompatible_inheritance_method"
|
|
|
|
)]
|
|
|
|
#[test_case(
|
2024-06-17 14:07:38 +08:00
|
|
|
&[
|
2021-09-12 04:34:30 +08:00
|
|
|
indoc! {"
|
|
|
|
class A(Generic[T]):
|
|
|
|
a: int32
|
|
|
|
b: T
|
|
|
|
c: A[int64]
|
|
|
|
def __init__(self, t: T):
|
|
|
|
self.a = 3
|
|
|
|
self.b = T
|
|
|
|
def fun(self, a: int32, b: T) -> list[virtual[B[bool]]]:
|
|
|
|
pass
|
|
|
|
"},
|
|
|
|
indoc! {"
|
|
|
|
class B(Generic[V], A[float]):
|
|
|
|
a: int32
|
|
|
|
def __init__(self):
|
|
|
|
pass
|
|
|
|
def fun(self, a: int32, b: T) -> list[virtual[B[bool]]]:
|
|
|
|
# override
|
|
|
|
pass
|
|
|
|
"}
|
|
|
|
],
|
2024-06-17 14:07:38 +08:00
|
|
|
&["field `a` has already declared in the ancestor classes"];
|
2021-09-12 04:34:30 +08:00
|
|
|
"err_incompatible_inheritance_field"
|
|
|
|
)]
|
2021-09-12 05:00:26 +08:00
|
|
|
#[test_case(
|
2024-06-17 14:07:38 +08:00
|
|
|
&[
|
2021-09-12 05:00:26 +08:00
|
|
|
indoc! {"
|
|
|
|
class A:
|
|
|
|
def __init__(self):
|
|
|
|
pass
|
|
|
|
"},
|
|
|
|
indoc! {"
|
|
|
|
class A:
|
|
|
|
a: int32
|
|
|
|
def __init__(self):
|
|
|
|
pass
|
|
|
|
"}
|
|
|
|
],
|
2024-06-17 14:07:38 +08:00
|
|
|
&["duplicate definition of class `A` (at unknown:1:1)"];
|
2021-09-12 05:00:26 +08:00
|
|
|
"class same name"
|
|
|
|
)]
|
2024-06-17 14:07:38 +08:00
|
|
|
fn test_analyze(source: &[&str], res: &[&str]) {
|
2021-09-10 21:26:39 +08:00
|
|
|
let print = false;
|
2024-08-12 18:00:23 +08:00
|
|
|
let mut composer =
|
|
|
|
TopLevelComposer::new(Vec::new(), Vec::new(), ComposerConfig::default(), 64).0;
|
2021-09-08 02:27:12 +08:00
|
|
|
|
2021-09-17 16:02:00 +08:00
|
|
|
let internal_resolver = make_internal_resolver_with_tvar(
|
2021-09-20 14:24:16 +08:00
|
|
|
vec![
|
2021-09-17 16:02:00 +08:00
|
|
|
("T".into(), vec![]),
|
|
|
|
("V".into(), vec![composer.primitives_ty.bool, composer.primitives_ty.int32]),
|
|
|
|
("G".into(), vec![composer.primitives_ty.bool, composer.primitives_ty.int64]),
|
|
|
|
],
|
|
|
|
&mut composer.unifier,
|
2021-09-20 14:24:16 +08:00
|
|
|
print,
|
2021-09-17 16:02:00 +08:00
|
|
|
);
|
2021-10-16 18:08:13 +08:00
|
|
|
let resolver =
|
|
|
|
Arc::new(Resolver(internal_resolver.clone())) as Arc<dyn SymbolResolver + Send + Sync>;
|
2021-09-07 10:03:31 +08:00
|
|
|
|
|
|
|
for s in source {
|
2024-06-17 14:07:38 +08:00
|
|
|
let ast = parse_program(s, FileName::default()).unwrap();
|
2021-09-07 10:03:31 +08:00
|
|
|
let ast = ast[0].clone();
|
|
|
|
|
2021-09-14 22:49:20 +08:00
|
|
|
let (id, def_id, ty) = {
|
2024-06-17 14:07:38 +08:00
|
|
|
match composer.register_top_level(ast, Some(resolver.clone()), "", false) {
|
2021-09-10 21:26:39 +08:00
|
|
|
Ok(x) => x,
|
|
|
|
Err(msg) => {
|
|
|
|
if print {
|
2024-06-17 14:07:38 +08:00
|
|
|
println!("{msg}");
|
2021-09-10 21:26:39 +08:00
|
|
|
} else {
|
|
|
|
assert_eq!(res[0], msg);
|
|
|
|
}
|
2021-09-12 04:40:40 +08:00
|
|
|
return;
|
2021-09-10 21:26:39 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2021-09-22 17:19:27 +08:00
|
|
|
internal_resolver.add_id_def(id, def_id);
|
2021-09-14 22:49:20 +08:00
|
|
|
if let Some(ty) = ty {
|
|
|
|
internal_resolver.add_id_type(id, ty);
|
|
|
|
}
|
2021-09-07 10:03:31 +08:00
|
|
|
}
|
|
|
|
|
2021-09-14 16:16:48 +08:00
|
|
|
if let Err(msg) = composer.start_analysis(false) {
|
2021-09-10 21:26:39 +08:00
|
|
|
if print {
|
2023-11-15 17:30:26 +08:00
|
|
|
println!("{}", msg.iter().sorted().join("\n----------\n"));
|
2021-09-10 21:26:39 +08:00
|
|
|
} else {
|
2023-11-15 17:30:26 +08:00
|
|
|
assert_eq!(res[0], msg.iter().next().unwrap());
|
2021-09-10 21:26:39 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// skip 5 to skip primitives
|
2021-10-03 16:39:12 +08:00
|
|
|
let mut res_vec: Vec<String> = Vec::new();
|
2021-12-02 10:45:46 +08:00
|
|
|
for (def, _) in composer.definition_ast_list.iter().skip(composer.builtin_num) {
|
2021-09-10 21:26:39 +08:00
|
|
|
let def = &*def.read();
|
2021-11-05 20:28:21 +08:00
|
|
|
res_vec.push(format!("{}\n", def.to_string(composer.unifier.borrow_mut())));
|
2021-09-10 21:26:39 +08:00
|
|
|
}
|
2021-10-03 16:39:12 +08:00
|
|
|
insta::assert_debug_snapshot!(res_vec);
|
2021-09-07 17:30:15 +08:00
|
|
|
}
|
2021-09-08 02:27:12 +08:00
|
|
|
}
|
2021-09-14 16:16:48 +08:00
|
|
|
|
|
|
|
#[test_case(
|
|
|
|
vec![
|
|
|
|
indoc! {"
|
|
|
|
def fun(a: int32, b: int32) -> int32:
|
|
|
|
return a + b
|
2021-09-14 22:49:20 +08:00
|
|
|
"},
|
|
|
|
indoc! {"
|
|
|
|
def fib(n: int32) -> int32:
|
|
|
|
if n <= 2:
|
|
|
|
return 1
|
|
|
|
a = fib(n - 1)
|
|
|
|
b = fib(n - 2)
|
|
|
|
return fib(n - 1)
|
2021-09-14 16:16:48 +08:00
|
|
|
"}
|
|
|
|
],
|
2024-06-17 14:07:38 +08:00
|
|
|
&[];
|
2021-09-14 16:16:48 +08:00
|
|
|
"simple function"
|
|
|
|
)]
|
|
|
|
#[test_case(
|
|
|
|
vec![
|
|
|
|
indoc! {"
|
|
|
|
class A:
|
|
|
|
a: int32
|
|
|
|
def __init__(self):
|
|
|
|
self.a = 3
|
|
|
|
def fun(self) -> int32:
|
|
|
|
b = self.a + 3
|
|
|
|
return b * self.a
|
2021-09-14 22:49:20 +08:00
|
|
|
def clone(self) -> A:
|
2021-09-14 16:16:48 +08:00
|
|
|
SELF = self
|
|
|
|
return SELF
|
2021-09-14 22:49:20 +08:00
|
|
|
def sum(self) -> int32:
|
|
|
|
if self.a == 0:
|
2021-09-21 02:48:42 +08:00
|
|
|
return self.a
|
2021-09-14 22:49:20 +08:00
|
|
|
else:
|
|
|
|
a = self.a
|
|
|
|
self.a = self.a - 1
|
|
|
|
return a + self.sum()
|
|
|
|
def fib(self, a: int32) -> int32:
|
|
|
|
if a <= 2:
|
|
|
|
return 1
|
|
|
|
return self.fib(a - 1) + self.fib(a - 2)
|
2021-09-14 16:16:48 +08:00
|
|
|
"},
|
|
|
|
indoc! {"
|
|
|
|
def fun(a: A) -> int32:
|
2021-09-14 22:49:20 +08:00
|
|
|
return a.fun() + 2
|
2021-09-14 16:16:48 +08:00
|
|
|
"}
|
|
|
|
],
|
2024-06-17 14:07:38 +08:00
|
|
|
&[];
|
2021-09-14 16:16:48 +08:00
|
|
|
"simple class body"
|
|
|
|
)]
|
2021-09-17 00:35:58 +08:00
|
|
|
#[test_case(
|
|
|
|
vec![
|
|
|
|
indoc! {"
|
2021-09-17 16:02:00 +08:00
|
|
|
def fun(a: V, c: G, t: T) -> V:
|
2021-09-17 01:47:54 +08:00
|
|
|
b = a
|
2021-09-17 16:02:00 +08:00
|
|
|
cc = c
|
|
|
|
ret = fun(b, cc, t)
|
|
|
|
return ret * ret
|
|
|
|
"},
|
|
|
|
indoc! {"
|
2021-09-21 02:48:42 +08:00
|
|
|
def sum_three(l: list[V]) -> V:
|
2021-09-17 16:02:00 +08:00
|
|
|
return l[0] + l[1] + l[2]
|
|
|
|
"},
|
|
|
|
indoc! {"
|
|
|
|
def sum_sq_pair(p: tuple[V, V]) -> list[V]:
|
|
|
|
a = p[0]
|
|
|
|
b = p[1]
|
|
|
|
a = a**a
|
|
|
|
b = b**b
|
|
|
|
return [a, b]
|
2021-09-17 00:35:58 +08:00
|
|
|
"}
|
|
|
|
],
|
2024-06-17 14:07:38 +08:00
|
|
|
&[];
|
2021-09-17 00:35:58 +08:00
|
|
|
"type var fun"
|
|
|
|
)]
|
2021-09-17 16:02:00 +08:00
|
|
|
#[test_case(
|
|
|
|
vec![
|
|
|
|
indoc! {"
|
|
|
|
class A(Generic[G]):
|
|
|
|
a: G
|
|
|
|
b: bool
|
|
|
|
def __init__(self, aa: G):
|
|
|
|
self.a = aa
|
2021-09-21 02:48:42 +08:00
|
|
|
if 2 > 1:
|
|
|
|
self.b = True
|
|
|
|
else:
|
|
|
|
# self.b = False
|
|
|
|
pass
|
2021-09-17 16:02:00 +08:00
|
|
|
def fun(self, a: G) -> list[G]:
|
|
|
|
ret = [a, self.a]
|
|
|
|
return ret if self.b else self.fun(self.a)
|
|
|
|
"}
|
|
|
|
],
|
2024-06-17 14:07:38 +08:00
|
|
|
&[];
|
2021-09-17 16:02:00 +08:00
|
|
|
"type var class"
|
|
|
|
)]
|
2021-09-21 02:48:42 +08:00
|
|
|
#[test_case(
|
|
|
|
vec![
|
|
|
|
indoc! {"
|
|
|
|
class A:
|
|
|
|
def fun(self):
|
2021-09-21 13:19:13 +08:00
|
|
|
pass
|
2021-09-21 02:48:42 +08:00
|
|
|
"},
|
|
|
|
indoc!{"
|
|
|
|
class B:
|
|
|
|
a: int32
|
|
|
|
b: bool
|
|
|
|
def __init__(self):
|
|
|
|
# self.b = False
|
|
|
|
if 3 > 2:
|
|
|
|
self.a = 3
|
|
|
|
self.b = False
|
|
|
|
else:
|
|
|
|
self.a = 4
|
|
|
|
self.b = True
|
|
|
|
"}
|
|
|
|
],
|
2024-06-17 14:07:38 +08:00
|
|
|
&[];
|
2021-09-21 02:48:42 +08:00
|
|
|
"no_init_inst_check"
|
|
|
|
)]
|
2024-06-17 14:07:38 +08:00
|
|
|
fn test_inference(source: Vec<&str>, res: &[&str]) {
|
2021-09-14 16:16:48 +08:00
|
|
|
let print = true;
|
2024-08-12 18:00:23 +08:00
|
|
|
let mut composer =
|
|
|
|
TopLevelComposer::new(Vec::new(), Vec::new(), ComposerConfig::default(), 64).0;
|
2021-09-14 16:16:48 +08:00
|
|
|
|
2021-09-17 16:02:00 +08:00
|
|
|
let internal_resolver = make_internal_resolver_with_tvar(
|
2021-09-20 14:24:16 +08:00
|
|
|
vec![
|
2021-09-17 16:02:00 +08:00
|
|
|
("T".into(), vec![]),
|
2021-09-20 14:24:16 +08:00
|
|
|
(
|
|
|
|
"V".into(),
|
|
|
|
vec![
|
|
|
|
composer.primitives_ty.float,
|
|
|
|
composer.primitives_ty.int32,
|
|
|
|
composer.primitives_ty.int64,
|
|
|
|
],
|
|
|
|
),
|
2021-09-17 16:02:00 +08:00
|
|
|
("G".into(), vec![composer.primitives_ty.bool, composer.primitives_ty.int64]),
|
|
|
|
],
|
|
|
|
&mut composer.unifier,
|
2021-09-20 14:24:16 +08:00
|
|
|
print,
|
2021-09-17 16:02:00 +08:00
|
|
|
);
|
2021-11-05 20:28:21 +08:00
|
|
|
let resolver =
|
|
|
|
Arc::new(Resolver(internal_resolver.clone())) as Arc<dyn SymbolResolver + Send + Sync>;
|
2021-09-14 16:16:48 +08:00
|
|
|
|
|
|
|
for s in source {
|
2024-06-17 14:07:38 +08:00
|
|
|
let ast = parse_program(s, FileName::default()).unwrap();
|
2021-09-14 16:16:48 +08:00
|
|
|
let ast = ast[0].clone();
|
|
|
|
|
2021-09-14 22:49:20 +08:00
|
|
|
let (id, def_id, ty) = {
|
2024-06-17 14:07:38 +08:00
|
|
|
match composer.register_top_level(ast, Some(resolver.clone()), "", false) {
|
2021-09-14 16:16:48 +08:00
|
|
|
Ok(x) => x,
|
|
|
|
Err(msg) => {
|
|
|
|
if print {
|
2024-06-17 14:07:38 +08:00
|
|
|
println!("{msg}");
|
2021-09-14 16:16:48 +08:00
|
|
|
} else {
|
|
|
|
assert_eq!(res[0], msg);
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2021-10-16 18:08:13 +08:00
|
|
|
internal_resolver.add_id_def(id, def_id);
|
2021-09-14 22:49:20 +08:00
|
|
|
if let Some(ty) = ty {
|
|
|
|
internal_resolver.add_id_type(id, ty);
|
|
|
|
}
|
2021-09-14 16:16:48 +08:00
|
|
|
}
|
2021-09-14 22:49:20 +08:00
|
|
|
|
2021-09-14 16:16:48 +08:00
|
|
|
if let Err(msg) = composer.start_analysis(true) {
|
|
|
|
if print {
|
2023-11-15 17:30:26 +08:00
|
|
|
println!("{}", msg.iter().sorted().join("\n----------\n"));
|
2021-09-14 16:16:48 +08:00
|
|
|
} else {
|
2023-11-15 17:30:26 +08:00
|
|
|
assert_eq!(res[0], msg.iter().next().unwrap());
|
2021-09-14 16:16:48 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// skip 5 to skip primitives
|
2021-09-20 14:24:16 +08:00
|
|
|
let mut stringify_folder = TypeToStringFolder { unifier: &mut composer.unifier };
|
2024-06-17 14:07:38 +08:00
|
|
|
for (def, _) in composer.definition_ast_list.iter().skip(composer.builtin_num) {
|
2021-09-14 16:16:48 +08:00
|
|
|
let def = &*def.read();
|
|
|
|
|
2021-09-14 22:49:20 +08:00
|
|
|
if let TopLevelDef::Function { instance_to_stmt, name, .. } = def {
|
2021-09-20 14:24:16 +08:00
|
|
|
println!(
|
|
|
|
"=========`{}`: number of instances: {}===========",
|
|
|
|
name,
|
|
|
|
instance_to_stmt.len()
|
|
|
|
);
|
2024-06-17 14:07:38 +08:00
|
|
|
for inst in instance_to_stmt {
|
2021-09-14 16:16:48 +08:00
|
|
|
let ast = &inst.1.body;
|
2021-09-22 17:19:27 +08:00
|
|
|
for b in ast.iter() {
|
2021-09-17 01:47:54 +08:00
|
|
|
println!("{:?}", stringify_folder.fold_stmt(b.clone()).unwrap());
|
2021-09-14 22:49:20 +08:00
|
|
|
println!("--------------------");
|
|
|
|
}
|
|
|
|
println!("\n");
|
2021-09-14 16:16:48 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-09-14 22:49:20 +08:00
|
|
|
}
|
2021-09-17 01:47:54 +08:00
|
|
|
|
2021-09-20 14:24:16 +08:00
|
|
|
fn make_internal_resolver_with_tvar(
|
2021-09-22 17:19:27 +08:00
|
|
|
tvars: Vec<(StrRef, Vec<Type>)>,
|
2021-09-20 14:24:16 +08:00
|
|
|
unifier: &mut Unifier,
|
|
|
|
print: bool,
|
|
|
|
) -> Arc<ResolverInternal> {
|
2024-07-02 11:05:05 +08:00
|
|
|
let list_elem_tvar = unifier.get_fresh_var(Some("list_elem".into()), None);
|
|
|
|
let list = unifier.add_ty(TypeEnum::TObj {
|
|
|
|
obj_id: PrimDef::List.id(),
|
|
|
|
fields: HashMap::new(),
|
|
|
|
params: into_var_map([list_elem_tvar]),
|
|
|
|
});
|
|
|
|
|
2021-09-17 16:02:00 +08:00
|
|
|
let res: Arc<ResolverInternal> = ResolverInternal {
|
2024-07-02 11:05:05 +08:00
|
|
|
id_to_def: Mutex::new(HashMap::from([("list".into(), PrimDef::List.id())])),
|
2021-09-17 16:02:00 +08:00
|
|
|
id_to_type: tvars
|
|
|
|
.into_iter()
|
2021-09-20 14:24:16 +08:00
|
|
|
.map(|(name, range)| {
|
2021-10-16 18:08:13 +08:00
|
|
|
(name, {
|
2024-06-13 13:28:39 +08:00
|
|
|
let tvar = unifier.get_fresh_var_with_range(range.as_slice(), None, None);
|
2021-09-17 16:02:00 +08:00
|
|
|
if print {
|
2024-06-13 13:28:39 +08:00
|
|
|
println!("{}: {:?}, typevar{}", name, tvar.ty, tvar.id);
|
2021-09-17 16:02:00 +08:00
|
|
|
}
|
2024-06-13 13:28:39 +08:00
|
|
|
tvar.ty
|
2021-09-20 14:24:16 +08:00
|
|
|
})
|
|
|
|
})
|
2021-09-17 16:02:00 +08:00
|
|
|
.collect::<HashMap<_, _>>()
|
|
|
|
.into(),
|
2024-07-02 11:05:05 +08:00
|
|
|
class_names: Mutex::new(HashMap::from([("list".into(), list)])),
|
2021-09-20 14:24:16 +08:00
|
|
|
}
|
|
|
|
.into();
|
2021-09-17 16:02:00 +08:00
|
|
|
if print {
|
|
|
|
println!();
|
|
|
|
}
|
|
|
|
res
|
|
|
|
}
|
|
|
|
|
2021-09-17 01:47:54 +08:00
|
|
|
struct TypeToStringFolder<'a> {
|
2021-09-20 14:24:16 +08:00
|
|
|
unifier: &'a mut Unifier,
|
2021-09-17 01:47:54 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Fold<Option<Type>> for TypeToStringFolder<'a> {
|
|
|
|
type TargetU = String;
|
|
|
|
type Error = String;
|
|
|
|
fn map_user(&mut self, user: Option<Type>) -> Result<Self::TargetU, Self::Error> {
|
|
|
|
Ok(if let Some(ty) = user {
|
2022-02-21 18:27:46 +08:00
|
|
|
self.unifier.internal_stringify(
|
|
|
|
ty,
|
2024-06-17 14:07:38 +08:00
|
|
|
&mut |id| format!("class{id}"),
|
|
|
|
&mut |id| format!("typevar{id}"),
|
2022-02-21 18:27:46 +08:00
|
|
|
&mut None,
|
|
|
|
)
|
2021-09-20 14:24:16 +08:00
|
|
|
} else {
|
|
|
|
"None".into()
|
|
|
|
})
|
2021-09-17 01:47:54 +08:00
|
|
|
}
|
2021-09-20 14:24:16 +08:00
|
|
|
}
|