Option type support #224
|
@ -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 "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)
|
||||||
|
|
|
@ -353,7 +353,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();
|
||||||
|
@ -376,7 +375,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(),
|
||||||
|
|
|
@ -282,24 +282,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 {
|
||||||
|
@ -597,14 +580,32 @@ 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)`, so the obj 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) => {
|
||||||
|
@ -845,36 +846,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();
|
||||||
|
|
||||||
|
|
|
@ -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!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -951,6 +935,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()),
|
||||||
|
|
|
@ -20,6 +20,8 @@ impl<'a> Inferencer<'a> {
|
||||||
defined_identifiers: &mut HashSet<StrRef>,
|
defined_identifiers: &mut HashSet<StrRef>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
match &pattern.node {
|
match &pattern.node {
|
||||||
|
ast::ExprKind::Name { id, .. } if id == &"none".into() =>
|
||||||
|
Err(format!("cannot assign to a `none` (at {})", pattern.location)),
|
||||||
ExprKind::Name { id, .. } => {
|
ExprKind::Name { id, .. } => {
|
||||||
if !defined_identifiers.contains(id) {
|
if !defined_identifiers.contains(id) {
|
||||||
defined_identifiers.insert(*id);
|
defined_identifiers.insert(*id);
|
||||||
|
@ -70,6 +72,9 @@ impl<'a> Inferencer<'a> {
|
||||||
}
|
}
|
||||||
match &expr.node {
|
match &expr.node {
|
||||||
ExprKind::Name { id, .. } => {
|
ExprKind::Name { id, .. } => {
|
||||||
|
if id == &"none".into() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
self.should_have_value(expr)?;
|
self.should_have_value(expr)?;
|
||||||
if !defined_identifiers.contains(id) {
|
if !defined_identifiers.contains(id) {
|
||||||
match self.function_data.resolver.get_symbol_type(
|
match self.function_data.resolver.get_symbol_type(
|
||||||
|
|
|
@ -449,25 +449,47 @@ impl<'a> fold::Fold<()> for Inferencer<'a> {
|
||||||
Some(self.infer_constant(value, &expr.location)?)
|
Some(self.infer_constant(value, &expr.location)?)
|
||||||
}
|
}
|
||||||
ast::ExprKind::Name { id, .. } => {
|
ast::ExprKind::Name { id, .. } => {
|
||||||
if !self.defined_identifiers.contains(id) {
|
// the name `none` is special since it may have different types
|
||||||
match self.function_data.resolver.get_symbol_type(
|
if id == &"none".into() {
|
||||||
self.unifier,
|
if let TypeEnum::TObj { params, .. } =
|
||||||
&self.top_level.definitions.read(),
|
self.unifier.get_ty_immutable(self.primitives.option).as_ref()
|
||||||
self.primitives,
|
{
|
||||||
*id,
|
let var_map = params
|
||||||
) {
|
.iter()
|
||||||
Ok(_) => {
|
.map(|(id_var, ty)| {
|
||||||
self.defined_identifiers.insert(*id);
|
if let TypeEnum::TVar { id, range, name, loc, .. } = &*self.unifier.get_ty(*ty) {
|
||||||
}
|
assert_eq!(*id, *id_var);
|
||||||
Err(e) => {
|
(*id, self.unifier.get_fresh_var_with_range(range, *name, *loc).0)
|
||||||
return report_error(
|
} else {
|
||||||
&format!("type error at identifier `{}` ({})", id, e),
|
unreachable!()
|
||||||
expr.location,
|
}
|
||||||
);
|
})
|
||||||
|
.collect::<HashMap<_, _>>();
|
||||||
|
Some(self.unifier.subst(self.primitives.option, &var_map).unwrap())
|
||||||
|
} else {
|
||||||
|
unreachable!("must be tobj")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if !self.defined_identifiers.contains(id) {
|
||||||
|
match self.function_data.resolver.get_symbol_type(
|
||||||
|
self.unifier,
|
||||||
|
&self.top_level.definitions.read(),
|
||||||
|
self.primitives,
|
||||||
|
*id,
|
||||||
|
) {
|
||||||
|
Ok(_) => {
|
||||||
|
self.defined_identifiers.insert(*id);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
return report_error(
|
||||||
|
&format!("type error at identifier `{}` ({})", id, e),
|
||||||
|
expr.location,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Some(self.infer_identifier(*id)?)
|
||||||
}
|
}
|
||||||
Some(self.infer_identifier(*id)?)
|
|
||||||
}
|
}
|
||||||
ast::ExprKind::List { elts, .. } => Some(self.infer_list(elts)?),
|
ast::ExprKind::List { elts, .. } => Some(self.infer_list(elts)?),
|
||||||
ast::ExprKind::Tuple { elts, .. } => Some(self.infer_tuple(elts)?),
|
ast::ExprKind::Tuple { elts, .. } => Some(self.infer_tuple(elts)?),
|
||||||
|
@ -933,17 +955,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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue