diff --git a/nac3core/src/codegen/mod.rs b/nac3core/src/codegen/mod.rs index fd96e5d90..6549b743c 100644 --- a/nac3core/src/codegen/mod.rs +++ b/nac3core/src/codegen/mod.rs @@ -385,6 +385,7 @@ pub fn gen_func<'ctx, G: CodeGenerator>( range: unifier.get_representative(primitives.range), str: unifier.get_representative(primitives.str), exception: unifier.get_representative(primitives.exception), + option: unifier.get_representative(primitives.option), }; let mut type_cache: HashMap<_, _> = [ @@ -417,6 +418,7 @@ pub fn gen_func<'ctx, G: CodeGenerator>( exception.set_body(&fields, false); exception.ptr_type(AddressSpace::Generic).into() }); + // TODO: insert option type cache let (args, ret) = if let ConcreteTypeEnum::TFunc { args, ret, .. } = task.store.get(task.signature) diff --git a/nac3core/src/toplevel/builtins.rs b/nac3core/src/toplevel/builtins.rs index 2d97d612f..bedb1323c 100644 --- a/nac3core/src/toplevel/builtins.rs +++ b/nac3core/src/toplevel/builtins.rs @@ -105,6 +105,20 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo { ("__param2__".into(), int64, true), ]; + // for Option, is_some and is_none share the same type: () -> bool, + // and they are methods under the same class `Option` + let (is_some_ty, unwrap_ty, (option_ty_var, option_ty_var_id)) = + if let TypeEnum::TObj { fields, params, .. } = + primitives.1.get_ty(primitives.0.option).as_ref() + { + ( + *fields.get(&"is_some".into()).unwrap(), + *fields.get(&"unwrap".into()).unwrap(), + (*params.iter().next().unwrap().1, *params.iter().next().unwrap().0), + ) + } else { + unreachable!() + }; let top_level_def_list = vec![ Arc::new(RwLock::new(TopLevelComposer::make_top_level_class_def( 0, @@ -180,6 +194,58 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo { None, None, ))), + Arc::new(RwLock::new({ + TopLevelDef::Class { + name: "Option".into(), + object_id: DefinitionId(10), + type_vars: vec![option_ty_var], + fields: vec![], + methods: vec![ + ("is_some".into(), is_some_ty.0, DefinitionId(11)), + ("is_none".into(), is_some_ty.0, DefinitionId(12)), + ("unwrap".into(), unwrap_ty.0, DefinitionId(13)), + ], + ancestors: vec![TypeAnnotation::CustomClass { + id: DefinitionId(10), + params: Default::default(), + }], + constructor: None, + resolver: None, + loc: None, + } + })), + Arc::new(RwLock::new(TopLevelDef::Function { + name: "Option.is_some".into(), + simple_name: "is_some".into(), + signature: is_some_ty.0, + var_id: vec![option_ty_var_id], + instance_to_symbol: Default::default(), + instance_to_stmt: Default::default(), + resolver: None, + codegen_callback: Some(Arc::new(GenCall::new(Box::new( + |ctx, _, fun, args, generator| { + // TODO: + unimplemented!() + }, + )))), + loc: None, + })), + Arc::new(RwLock::new(TopLevelDef::Function { + name: "Option.unwrap".into(), + simple_name: "unwrap".into(), + signature: unwrap_ty.0, + var_id: vec![option_ty_var_id], + instance_to_symbol: Default::default(), + instance_to_stmt: Default::default(), + resolver: None, + codegen_callback: Some(Arc::new(GenCall::new(Box::new( + |ctx, _, fun, args, generator| { + // TODO: + unimplemented!() + }, + )))), + loc: None, + })), Arc::new(RwLock::new(TopLevelDef::Function { name: "int32".into(), simple_name: "int32".into(), @@ -1098,6 +1164,25 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo { )))), loc: None, })), + Arc::new(RwLock::new(TopLevelDef::Function { + name: "Some".into(), + simple_name: "Some".into(), + signature: primitives.1.add_ty(TypeEnum::TFunc(FunSignature { + args: vec![FuncArg { name: "n".into(), ty: option_ty_var, default_value: None }], + ret: primitives.0.option, + vars: HashMap::from([(option_ty_var_id, option_ty_var)]), + })), + var_id: vec![option_ty_var_id], + instance_to_symbol: Default::default(), + instance_to_stmt: Default::default(), + resolver: None, + codegen_callback: Some(Arc::new(GenCall::new(Box::new( + |ctx, _, fun, args, generator| { + unimplemented!() + }, + )))), + loc: None, + })), ]; let ast_list: Vec>> = @@ -1123,6 +1208,7 @@ pub fn get_builtins(primitives: &mut (PrimitiveStore, Unifier)) -> BuiltinInfo { "min", "max", "abs", + "Some", ], ) } diff --git a/nac3core/src/toplevel/composer.rs b/nac3core/src/toplevel/composer.rs index 991926a62..910bfe027 100644 --- a/nac3core/src/toplevel/composer.rs +++ b/nac3core/src/toplevel/composer.rs @@ -74,6 +74,8 @@ impl TopLevelComposer { "self".into(), "Kernel".into(), "KernelInvariant".into(), + "Some".into(), + "Option".into(), ]); let defined_names: HashSet = Default::default(); let method_class: HashMap = Default::default(); @@ -92,7 +94,6 @@ impl TopLevelComposer { } else if let TopLevelDef::Class { name, constructor, object_id, type_vars, .. } = &*def { assert!(id == object_id.0); - assert!(type_vars.is_empty()); if let Some(constructor) = constructor { builtin_ty.insert(*name, *constructor); } diff --git a/nac3core/src/toplevel/helper.rs b/nac3core/src/toplevel/helper.rs index a76e7b3a7..0c00344be 100644 --- a/nac3core/src/toplevel/helper.rs +++ b/nac3core/src/toplevel/helper.rs @@ -107,7 +107,42 @@ impl TopLevelComposer { fields: HashMap::new(), params: HashMap::new(), }); - let primitives = PrimitiveStore { int32, int64, float, bool, none, range, str, exception, uint32, uint64 }; + + let option_type_var = unifier.get_fresh_var(Some("option_type_var".into()), None); + let is_some_type_fun_ty = unifier.add_ty(TypeEnum::TFunc(FunSignature { + args: vec![], + ret: bool, + vars: HashMap::from([(option_type_var.1, option_type_var.0)]), + })); + let unwrap_fun_ty = unifier.add_ty(TypeEnum::TFunc(FunSignature { + args: vec![], + ret: option_type_var.0, + vars: HashMap::from([(option_type_var.1, option_type_var.0)]), + })); + let option = unifier.add_ty(TypeEnum::TObj { + obj_id: DefinitionId(10), + fields: vec![ + ("is_some".into(), (is_some_type_fun_ty, true)), + ("unwrap".into(), (unwrap_fun_ty, true)), + ] + .into_iter() + .collect::>(), + params: HashMap::from([(option_type_var.1, option_type_var.0)]), + }); + + let primitives = PrimitiveStore { + int32, + int64, + float, + bool, + none, + range, + str, + exception, + uint32, + uint64, + option, + }; crate::typecheck::magic_methods::set_primitives_magic_methods(&primitives, &mut unifier); (primitives, unifier) } diff --git a/nac3core/src/toplevel/type_annotation.rs b/nac3core/src/toplevel/type_annotation.rs index 3f5405db8..337ec9d6e 100644 --- a/nac3core/src/toplevel/type_annotation.rs +++ b/nac3core/src/toplevel/type_annotation.rs @@ -223,6 +223,29 @@ pub fn parse_ast_to_type_annotation_kinds( Ok(TypeAnnotation::List(def_ann.into())) } + // option + ast::ExprKind::Subscript { value, slice, .. } + if { + matches!(&value.node, ast::ExprKind::Name { id, .. } if id == &"Option".into()) + } => + { + let def_ann = parse_ast_to_type_annotation_kinds( + resolver, + top_level_defs, + unifier, + primitives, + slice.as_ref(), + locked, + )?; + let id = + if let TypeEnum::TObj { obj_id, .. } = unifier.get_ty(primitives.option).as_ref() { + *obj_id + } else { + unreachable!() + }; + Ok(TypeAnnotation::CustomClass { id, params: vec![def_ann] }) + } + // tuple ast::ExprKind::Subscript { value, slice, .. } if { diff --git a/nac3core/src/typecheck/type_inferencer/mod.rs b/nac3core/src/typecheck/type_inferencer/mod.rs index c012f2ed5..b07e3d769 100644 --- a/nac3core/src/typecheck/type_inferencer/mod.rs +++ b/nac3core/src/typecheck/type_inferencer/mod.rs @@ -40,6 +40,7 @@ pub struct PrimitiveStore { pub range: Type, pub str: Type, pub exception: Type, + pub option: Type, } pub struct FunctionData { @@ -932,6 +933,17 @@ impl<'a> Inferencer<'a> { Ok(self.unifier.add_ty(TypeEnum::TTuple { ty: ty? })) } ast::Constant::Str(_) => Ok(self.primitives.str), + ast::Constant::None => { + let option_ty = self.primitives.option; + let new_mapping = if let TypeEnum::TObj { params, .. } = &*self.unifier.get_ty_immutable(option_ty) { + let (id, _) = params.iter().next().unwrap(); + // None can be Option[Any] + vec![(*id, self.unifier.get_fresh_var(None, None).0)].into_iter().collect() + } else { + unreachable!("option must be tobj") + }; + Ok(self.unifier.subst(option_ty, &new_mapping).unwrap()) + } _ => report_error("not supported", *loc), } }