nac3core/artiq: use none instead of None

This commit is contained in:
ychenfo 2022-03-23 05:10:18 +08:00
parent be66225665
commit 6e4b507d92
5 changed files with 104 additions and 84 deletions

View File

@ -11,7 +11,7 @@ from embedding_map import EmbeddingMap
__all__ = [ __all__ = [
"Kernel", "KernelInvariant", "virtual", "Kernel", "KernelInvariant", "virtual",
"Option", "Some", "Option", "Some", "none"
"round64", "floor64", "ceil64", "round64", "floor64", "ceil64",
"extern", "kernel", "portable", "nac3", "extern", "kernel", "portable", "nac3",
"rpc", "ms", "us", "ns", "rpc", "ms", "us", "ns",
@ -50,19 +50,20 @@ class Option(Generic[T]):
def __repr__(self) -> str: def __repr__(self) -> str:
if self.is_none(): if self.is_none():
return "Option(None)" return "Option(none)"
else: else:
return "Some({})".format(repr(self._nac3_option)) return "Some({})".format(repr(self._nac3_option))
def __str__(self) -> str: def __str__(self) -> str:
if self.is_none(): if self.is_none():
return "None" return "none"
else: else:
return "Some({})".format(str(self._nac3_option)) return "Some({})".format(str(self._nac3_option))
def Some(v: T) -> Option[T]: def Some(v: T) -> Option[T]:
return Option(v) return Option(v)
none = Option(None)
def round64(x): def round64(x):
return round(x) return round(x)

View File

@ -352,7 +352,6 @@ impl Nac3 {
let builtins_mod = PyModule::import(py, "builtins").unwrap(); let builtins_mod = PyModule::import(py, "builtins").unwrap();
let id_fn = builtins_mod.getattr("id").unwrap(); let id_fn = builtins_mod.getattr("id").unwrap();
let type_fn = builtins_mod.getattr("type").unwrap();
let numpy_mod = PyModule::import(py, "numpy").unwrap(); let numpy_mod = PyModule::import(py, "numpy").unwrap();
let typing_mod = PyModule::import(py, "typing").unwrap(); let typing_mod = PyModule::import(py, "typing").unwrap();
let types_mod = PyModule::import(py, "types").unwrap(); let types_mod = PyModule::import(py, "types").unwrap();
@ -375,7 +374,13 @@ impl Nac3 {
get_attr_id(types_mod, "GenericAlias"), get_attr_id(types_mod, "GenericAlias"),
), ),
none: id_fn none: id_fn
.call1((type_fn.call1((builtins_mod.getattr("None").unwrap(),)).unwrap(),)) .call1((builtins_mod
.getattr("globals")
.unwrap()
.call0()
.unwrap()
.get_item("none")
.unwrap(),))
.unwrap() .unwrap()
.extract() .extract()
.unwrap(), .unwrap(),

View File

@ -263,24 +263,7 @@ impl InnerResolver {
} else if ty_id == self.primitive_ids.option { } else if ty_id == self.primitive_ids.option {
Ok(Ok((primitives.option, false))) Ok(Ok((primitives.option, false)))
} else if ty_id == self.primitive_ids.none { } else if ty_id == self.primitive_ids.none {
if let TypeEnum::TObj { params, .. } = unreachable!("none cannot be typeid")
unifier.get_ty_immutable(primitives.option).as_ref()
{
let var_map = params
.iter()
.map(|(id_var, ty)| {
if let TypeEnum::TVar { id, range, name, loc, .. } = &*unifier.get_ty(*ty) {
assert_eq!(*id, *id_var);
(*id, unifier.get_fresh_var_with_range(range, *name, *loc).0)
} else {
unreachable!()
}
})
.collect::<HashMap<_, _>>();
Ok(Ok((unifier.subst(primitives.option, &var_map).unwrap(), true)))
} else {
unreachable!("must be tobj")
}
} else if let Some(def_id) = self.pyid_to_def.read().get(&ty_id).cloned() { } else if let Some(def_id) = self.pyid_to_def.read().get(&ty_id).cloned() {
let def = defs[def_id.0].read(); let def = defs[def_id.0].read();
if let TopLevelDef::Class { object_id, type_vars, fields, methods, .. } = &*def { if let TopLevelDef::Class { object_id, type_vars, fields, methods, .. } = &*def {
@ -566,14 +549,33 @@ impl InnerResolver {
{ {
let field_data = match obj.getattr("_nac3_option") { let field_data = match obj.getattr("_nac3_option") {
Ok(d) => d, Ok(d) => d,
// None should be already handled above // we use `none = Option(None)` now, so the obj should always have
// attr `_nac3_option`
Err(_) => unreachable!("cannot be None") Err(_) => unreachable!("cannot be None")
}; };
let field_obj_id: u64 = self.helper.id_fn.call1(py, (field_data,))?.extract(py)?; // if is `none`
let zelf_obj_id: u64 = self.helper.id_fn.call1(py, (obj,))?.extract(py)?; let zelf_id: u64 = self.helper.id_fn.call1(py, (obj,))?.extract(py)?;
if field_obj_id == zelf_obj_id { if zelf_id == self.primitive_ids.none {
return Ok(Err("self recursive option type is not allowed".into())) if let TypeEnum::TObj { params, .. } =
unifier.get_ty_immutable(primitives.option).as_ref()
{
let var_map = params
.iter()
.map(|(id_var, ty)| {
if let TypeEnum::TVar { id, range, name, loc, .. } = &*unifier.get_ty(*ty) {
assert_eq!(*id, *id_var);
(*id, unifier.get_fresh_var_with_range(range, *name, *loc).0)
} else {
unreachable!()
}
})
.collect::<HashMap<_, _>>();
return Ok(Ok(unifier.subst(primitives.option, &var_map).unwrap()))
} else {
unreachable!("must be tobj")
}
} }
let ty = match self.get_obj_type(py, field_data, unifier, defs, primitives)? { let ty = match self.get_obj_type(py, field_data, unifier, defs, primitives)? {
Ok(t) => t, Ok(t) => t,
Err(e) => { Err(e) => {
@ -814,36 +816,38 @@ impl InnerResolver {
global.set_initializer(&val); global.set_initializer(&val);
Ok(Some(global.as_pointer_value().into())) Ok(Some(global.as_pointer_value().into()))
} else if ty_id == self.primitive_ids.option { } else if ty_id == self.primitive_ids.option {
match self if id == self.primitive_ids.none {
.get_obj_value(py, obj.getattr("_nac3_option").unwrap(), ctx, generator) // for option type, just a null ptr, whose type needs to be casted in codegen
.map_err(|e| { // according to the type info attached in the ast
super::CompileError::new_err(format!( Ok(Some(ctx.ctx.i8_type().ptr_type(AddressSpace::Generic).const_null().into()))
"Error getting value of Option object: {}", } else {
e match self
)) .get_obj_value(py, obj.getattr("_nac3_option").unwrap(), ctx, generator)
})? { .map_err(|e| {
Some(v) => { super::CompileError::new_err(format!(
let global_str = format!("{}_option", id); "Error getting value of Option object: {}",
{ e
if self.global_value_ids.read().contains(&id) { ))
let global = ctx.module.get_global(&global_str).unwrap_or_else(|| { })? {
ctx.module.add_global(v.get_type(), Some(AddressSpace::Generic), &global_str) Some(v) => {
}); let global_str = format!("{}_option", id);
return Ok(Some(global.as_pointer_value().into())); {
} else { if self.global_value_ids.read().contains(&id) {
self.global_value_ids.write().insert(id); let global = ctx.module.get_global(&global_str).unwrap_or_else(|| {
ctx.module.add_global(v.get_type(), Some(AddressSpace::Generic), &global_str)
});
return Ok(Some(global.as_pointer_value().into()));
} else {
self.global_value_ids.write().insert(id);
}
} }
} let global = ctx.module.add_global(v.get_type(), Some(AddressSpace::Generic), &global_str);
let global = ctx.module.add_global(v.get_type(), Some(AddressSpace::Generic), &global_str); global.set_initializer(&v);
global.set_initializer(&v); Ok(Some(global.as_pointer_value().into()))
Ok(Some(global.as_pointer_value().into())) },
}, None => Ok(None),
None => Ok(None), }
} }
} else if ty_id == self.primitive_ids.none {
// for option type, just a null ptr, whose type needs to be casted in codegen
// according to the type info attached in the ast
Ok(Some(ctx.ctx.i8_type().ptr_type(AddressSpace::Generic).const_null().into()))
} else { } else {
let id_str = id.to_string(); let id_str = id.to_string();

View File

@ -200,22 +200,6 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
val val
} }
} }
Constant::None => {
match (
self.unifier.get_ty(ty).as_ref(),
self.unifier.get_ty(self.primitives.option).as_ref(),
) {
(
TypeEnum::TObj { obj_id, params, .. },
TypeEnum::TObj { obj_id: opt_id, .. },
) if *obj_id == *opt_id => self
.get_llvm_type(generator, *params.iter().next().unwrap().1)
.ptr_type(AddressSpace::Generic)
.const_null()
.into(),
_ => unreachable!("must be option type"),
}
}
_ => unreachable!(), _ => unreachable!(),
} }
} }
@ -945,6 +929,22 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
let ty = expr.custom.unwrap(); let ty = expr.custom.unwrap();
ctx.gen_const(generator, value, ty).into() ctx.gen_const(generator, value, ty).into()
} }
ExprKind::Name { id, .. } if id == &"none".into() => {
match (
ctx.unifier.get_ty(expr.custom.unwrap()).as_ref(),
ctx.unifier.get_ty(ctx.primitives.option).as_ref(),
) {
(
TypeEnum::TObj { obj_id, params, .. },
TypeEnum::TObj { obj_id: opt_id, .. },
) if *obj_id == *opt_id => ctx
.get_llvm_type(generator, *params.iter().next().unwrap().1)
.ptr_type(AddressSpace::Generic)
.const_null()
.into(),
_ => unreachable!("must be option type"),
}
}
ExprKind::Name { id, .. } => match ctx.var_assignment.get(id) { ExprKind::Name { id, .. } => match ctx.var_assignment.get(id) {
Some((ptr, None, _)) => ctx.builder.build_load(*ptr, "load").into(), Some((ptr, None, _)) => ctx.builder.build_load(*ptr, "load").into(),
Some((_, Some(static_value), _)) => ValueEnum::Static(static_value.clone()), Some((_, Some(static_value), _)) => ValueEnum::Static(static_value.clone()),

View File

@ -897,7 +897,26 @@ impl<'a> Inferencer<'a> {
} }
fn infer_identifier(&mut self, id: StrRef) -> InferenceResult { fn infer_identifier(&mut self, id: StrRef) -> InferenceResult {
if let Some(ty) = self.variable_mapping.get(&id) { if id == "none".into() {
if let TypeEnum::TObj { params, .. } =
self.unifier.get_ty_immutable(self.primitives.option).as_ref()
{
let var_map = params
.iter()
.map(|(id_var, ty)| {
if let TypeEnum::TVar { id, range, name, loc, .. } = &*self.unifier.get_ty(*ty) {
assert_eq!(*id, *id_var);
(*id, self.unifier.get_fresh_var_with_range(range, *name, *loc).0)
} else {
unreachable!()
}
})
.collect::<HashMap<_, _>>();
Ok(self.unifier.subst(self.primitives.option, &var_map).unwrap())
} else {
unreachable!("must be tobj")
}
} else if let Some(ty) = self.variable_mapping.get(&id) {
Ok(*ty) Ok(*ty)
} else { } else {
let variable_mapping = &mut self.variable_mapping; let variable_mapping = &mut self.variable_mapping;
@ -933,17 +952,8 @@ impl<'a> Inferencer<'a> {
Ok(self.unifier.add_ty(TypeEnum::TTuple { ty: ty? })) Ok(self.unifier.add_ty(TypeEnum::TTuple { ty: ty? }))
} }
ast::Constant::Str(_) => Ok(self.primitives.str), ast::Constant::Str(_) => Ok(self.primitives.str),
ast::Constant::None => { ast::Constant::None
let option_ty = self.primitives.option; => report_error("CPython `None` not supported (nac3 uses `none` instead)", *loc),
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), _ => report_error("not supported", *loc),
} }
} }