Compare commits

..

3 Commits

Author SHA1 Message Date
4948395ca2 core/toplevel/type_annotation: Add handling for mismatching class def
Primitive types only contain fields in its Type and not its TopLevelDef.
This causes primitive object types to lack some fields.
2024-07-19 14:42:14 +08:00
3db3061d99 artiq/symbol_resolver: Handle type of zero-length lists 2024-07-19 14:42:14 +08:00
51c2175c80 core/codegen/stmt: Convert assertion values to i1 2024-07-19 14:42:14 +08:00
9 changed files with 207 additions and 114 deletions

View File

@ -0,0 +1,24 @@
from min_artiq import *
from numpy import int32
@nac3
class EmptyList:
core: KernelInvariant[Core]
def __init__(self):
self.core = Core()
@rpc
def get_empty(self) -> list[int32]:
return []
@kernel
def run(self):
a: list[int32] = self.get_empty()
if a != []:
raise ValueError
if __name__ == "__main__":
EmptyList().run()

View File

@ -991,8 +991,15 @@ impl InnerResolver {
} }
_ => unreachable!("must be list"), _ => unreachable!("must be list"),
}; };
let ty = ctx.get_llvm_type(generator, elem_ty);
let size_t = generator.get_size_type(ctx.ctx); let size_t = generator.get_size_type(ctx.ctx);
let ty = if len == 0
&& matches!(&*ctx.unifier.get_ty_immutable(elem_ty), TypeEnum::TVar { .. })
{
// The default type for zero-length lists of unknown element type is size_t
size_t.into()
} else {
ctx.get_llvm_type(generator, elem_ty)
};
let arr_ty = ctx let arr_ty = ctx
.ctx .ctx
.struct_type(&[ty.ptr_type(AddressSpace::default()).into(), size_t.into()], false); .struct_type(&[ty.ptr_type(AddressSpace::default()).into(), size_t.into()], false);

View File

@ -951,9 +951,9 @@ pub fn destructure_range<'ctx>(
/// Allocates a List structure with the given [type][ty] and [length]. The name of the resulting /// Allocates a List structure with the given [type][ty] and [length]. The name of the resulting
/// LLVM value is `{name}.addr`, or `list.addr` if [name] is not specified. /// LLVM value is `{name}.addr`, or `list.addr` if [name] is not specified.
/// ///
/// Setting `ty` to [`None`] implies that the list does not have a known element type, which is only /// Setting `ty` to [`None`] implies that the list is empty **and** does not have a known element
/// valid for empty lists. It is undefined behavior to generate a sized list with an unknown element /// type, and will therefore set the `list.data` type as `size_t*`. It is undefined behavior to
/// type. /// generate a sized list with an unknown element type.
pub fn allocate_list<'ctx, G: CodeGenerator + ?Sized>( pub fn allocate_list<'ctx, G: CodeGenerator + ?Sized>(
generator: &mut G, generator: &mut G,
ctx: &mut CodeGenContext<'ctx, '_>, ctx: &mut CodeGenContext<'ctx, '_>,

View File

@ -1637,7 +1637,7 @@ pub fn gen_stmt<G: CodeGenerator>(
}; };
ctx.make_assert_impl( ctx.make_assert_impl(
generator, generator,
test.into_int_value(), generator.bool_to_i1(ctx, test.into_int_value()),
"0:AssertionError", "0:AssertionError",
err_msg, err_msg,
[None, None, None], [None, None, None],

View File

@ -564,7 +564,7 @@ impl<'a> BuiltinBuilder<'a> {
match (&tld, prim.details()) { match (&tld, prim.details()) {
( (
TopLevelDef::Class { name, object_id, .. }, TopLevelDef::Class { name, object_id, .. },
PrimDefDetails::PrimClass { name: exp_name }, PrimDefDetails::PrimClass { name: exp_name, .. },
) => { ) => {
let exp_object_id = prim.id(); let exp_object_id = prim.id();
assert_eq!(name, &exp_name.into()); assert_eq!(name, &exp_name.into());

View File

@ -766,6 +766,7 @@ impl TopLevelComposer {
let target_ty = get_type_from_type_annotation_kinds( let target_ty = get_type_from_type_annotation_kinds(
&temp_def_list, &temp_def_list,
unifier, unifier,
primitives,
&def, &def,
&mut subst_list, &mut subst_list,
)?; )?;
@ -936,6 +937,7 @@ impl TopLevelComposer {
let ty = get_type_from_type_annotation_kinds( let ty = get_type_from_type_annotation_kinds(
temp_def_list.as_ref(), temp_def_list.as_ref(),
unifier, unifier,
primitives_store,
&type_annotation, &type_annotation,
&mut None, &mut None,
)?; )?;
@ -1002,6 +1004,7 @@ impl TopLevelComposer {
get_type_from_type_annotation_kinds( get_type_from_type_annotation_kinds(
&temp_def_list, &temp_def_list,
unifier, unifier,
primitives_store,
&return_ty_annotation, &return_ty_annotation,
&mut None, &mut None,
)? )?
@ -1622,6 +1625,7 @@ impl TopLevelComposer {
let self_type = get_type_from_type_annotation_kinds( let self_type = get_type_from_type_annotation_kinds(
&def_list, &def_list,
unifier, unifier,
primitives_ty,
&make_self_type_annotation(type_vars, *object_id), &make_self_type_annotation(type_vars, *object_id),
&mut None, &mut None,
)?; )?;
@ -1803,7 +1807,11 @@ impl TopLevelComposer {
let ty_ann = make_self_type_annotation(type_vars, *class_id); let ty_ann = make_self_type_annotation(type_vars, *class_id);
let self_ty = get_type_from_type_annotation_kinds( let self_ty = get_type_from_type_annotation_kinds(
&def_list, unifier, &ty_ann, &mut None, &def_list,
unifier,
primitives_ty,
&ty_ann,
&mut None,
)?; )?;
vars.extend(type_vars.iter().map(|ty| { vars.extend(type_vars.iter().map(|ty| {
let TypeEnum::TVar { id, .. } = &*unifier.get_ty(*ty) else { let TypeEnum::TVar { id, .. } = &*unifier.get_ty(*ty) else {

View File

@ -113,7 +113,7 @@ pub enum PrimDef {
/// Associated details of a [`PrimDef`] /// Associated details of a [`PrimDef`]
pub enum PrimDefDetails { pub enum PrimDefDetails {
PrimFunction { name: &'static str, simple_name: &'static str }, PrimFunction { name: &'static str, simple_name: &'static str },
PrimClass { name: &'static str }, PrimClass { name: &'static str, get_ty_fn: fn(&PrimitiveStore) -> Type },
} }
impl PrimDef { impl PrimDef {
@ -155,15 +155,17 @@ impl PrimDef {
#[must_use] #[must_use]
pub fn name(&self) -> &'static str { pub fn name(&self) -> &'static str {
match self.details() { match self.details() {
PrimDefDetails::PrimFunction { name, .. } | PrimDefDetails::PrimClass { name } => name, PrimDefDetails::PrimFunction { name, .. } | PrimDefDetails::PrimClass { name, .. } => {
name
}
} }
} }
/// Get the associated details of this [`PrimDef`] /// Get the associated details of this [`PrimDef`]
#[must_use] #[must_use]
pub fn details(self) -> PrimDefDetails { pub fn details(self) -> PrimDefDetails {
fn class(name: &'static str) -> PrimDefDetails { fn class(name: &'static str, get_ty_fn: fn(&PrimitiveStore) -> Type) -> PrimDefDetails {
PrimDefDetails::PrimClass { name } PrimDefDetails::PrimClass { name, get_ty_fn }
} }
fn fun(name: &'static str, simple_name: Option<&'static str>) -> PrimDefDetails { fn fun(name: &'static str, simple_name: Option<&'static str>) -> PrimDefDetails {
@ -171,22 +173,22 @@ impl PrimDef {
} }
match self { match self {
PrimDef::Int32 => class("int32"), PrimDef::Int32 => class("int32", |primitives| primitives.int32),
PrimDef::Int64 => class("int64"), PrimDef::Int64 => class("int64", |primitives| primitives.int64),
PrimDef::Float => class("float"), PrimDef::Float => class("float", |primitives| primitives.float),
PrimDef::Bool => class("bool"), PrimDef::Bool => class("bool", |primitives| primitives.bool),
PrimDef::None => class("none"), PrimDef::None => class("none", |primitives| primitives.none),
PrimDef::Range => class("range"), PrimDef::Range => class("range", |primitives| primitives.range),
PrimDef::Str => class("str"), PrimDef::Str => class("str", |primitives| primitives.str),
PrimDef::Exception => class("Exception"), PrimDef::Exception => class("Exception", |primitives| primitives.exception),
PrimDef::UInt32 => class("uint32"), PrimDef::UInt32 => class("uint32", |primitives| primitives.uint32),
PrimDef::UInt64 => class("uint64"), PrimDef::UInt64 => class("uint64", |primitives| primitives.uint64),
PrimDef::Option => class("Option"), PrimDef::Option => class("Option", |primitives| primitives.option),
PrimDef::OptionIsSome => fun("Option.is_some", Some("is_some")), PrimDef::OptionIsSome => fun("Option.is_some", Some("is_some")),
PrimDef::OptionIsNone => fun("Option.is_none", Some("is_none")), PrimDef::OptionIsNone => fun("Option.is_none", Some("is_none")),
PrimDef::OptionUnwrap => fun("Option.unwrap", Some("unwrap")), PrimDef::OptionUnwrap => fun("Option.unwrap", Some("unwrap")),
PrimDef::List => class("list"), PrimDef::List => class("list", |primitives| primitives.list),
PrimDef::NDArray => class("ndarray"), PrimDef::NDArray => class("ndarray", |primitives| primitives.ndarray),
PrimDef::NDArrayCopy => fun("ndarray.copy", Some("copy")), PrimDef::NDArrayCopy => fun("ndarray.copy", Some("copy")),
PrimDef::NDArrayFill => fun("ndarray.fill", Some("fill")), PrimDef::NDArrayFill => fun("ndarray.fill", Some("fill")),
PrimDef::FunInt32 => fun("int32", None), PrimDef::FunInt32 => fun("int32", None),

View File

@ -1,8 +1,9 @@
use super::*; use super::*;
use crate::symbol_resolver::SymbolValue; use crate::symbol_resolver::SymbolValue;
use crate::toplevel::helper::PrimDef; use crate::toplevel::helper::{PrimDef, PrimDefDetails};
use crate::typecheck::typedef::VarMap; use crate::typecheck::typedef::VarMap;
use nac3parser::ast::Constant; use nac3parser::ast::Constant;
use strum::IntoEnumIterator;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum TypeAnnotation { pub enum TypeAnnotation {
@ -357,6 +358,7 @@ pub fn parse_ast_to_type_annotation_kinds<T, S: std::hash::BuildHasher + Clone>(
pub fn get_type_from_type_annotation_kinds( pub fn get_type_from_type_annotation_kinds(
top_level_defs: &[Arc<RwLock<TopLevelDef>>], top_level_defs: &[Arc<RwLock<TopLevelDef>>],
unifier: &mut Unifier, unifier: &mut Unifier,
primitives: &PrimitiveStore,
ann: &TypeAnnotation, ann: &TypeAnnotation,
subst_list: &mut Option<Vec<Type>>, subst_list: &mut Option<Vec<Type>>,
) -> Result<Type, HashSet<String>> { ) -> Result<Type, HashSet<String>> {
@ -379,10 +381,43 @@ pub fn get_type_from_type_annotation_kinds(
let param_ty = params let param_ty = params
.iter() .iter()
.map(|x| { .map(|x| {
get_type_from_type_annotation_kinds(top_level_defs, unifier, x, subst_list) get_type_from_type_annotation_kinds(
top_level_defs,
unifier,
primitives,
x,
subst_list,
)
}) })
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
let ty = if let Some(prim_def) = PrimDef::iter().find(|prim| prim.id() == *obj_id) {
// Primitive TopLevelDefs do not contain all fields that are present in their Type
// counterparts, so directly perform subst on the Type instead.
let PrimDefDetails::PrimClass { get_ty_fn, .. } = prim_def.details() else {
unreachable!()
};
let base_ty = get_ty_fn(primitives);
let params =
if let TypeEnum::TObj { params, .. } = &*unifier.get_ty_immutable(base_ty) {
params.clone()
} else {
unreachable!()
};
unifier
.subst(
get_ty_fn(primitives),
&params
.iter()
.zip(param_ty)
.map(|(obj_tv, param)| (*obj_tv.0, param))
.collect(),
)
.unwrap_or(base_ty)
} else {
let subst = { let subst = {
// check for compatible range // check for compatible range
// TODO: if allow type var to be applied(now this disallowed in the parse_to_type_annotation), need more check // TODO: if allow type var to be applied(now this disallowed in the parse_to_type_annotation), need more check
@ -424,12 +459,15 @@ pub fn get_type_from_type_annotation_kinds(
} }
} }
TypeEnum::TVar { id, range, name, loc, is_const_generic: true, .. } => { TypeEnum::TVar {
id, range, name, loc, is_const_generic: true, ..
} => {
let ty = range[0]; let ty = range[0];
let ok: bool = { let ok: bool = {
// create a temp type var and unify to check compatibility // create a temp type var and unify to check compatibility
p == *tvar || { p == *tvar || {
let temp = unifier.get_fresh_const_generic_var(ty, *name, *loc); let temp =
unifier.get_fresh_const_generic_var(ty, *name, *loc);
unifier.unify(temp.ty, p).is_ok() unifier.unify(temp.ty, p).is_ok()
} }
}; };
@ -468,11 +506,16 @@ pub fn get_type_from_type_annotation_kinds(
fields: tobj_fields, fields: tobj_fields,
params: subst, params: subst,
}); });
if need_subst { if need_subst {
if let Some(wl) = subst_list.as_mut() { if let Some(wl) = subst_list.as_mut() {
wl.push(ty); wl.push(ty);
} }
} }
ty
};
Ok(ty) Ok(ty)
} }
TypeAnnotation::Primitive(ty) | TypeAnnotation::TypeVar(ty) => Ok(*ty), TypeAnnotation::Primitive(ty) | TypeAnnotation::TypeVar(ty) => Ok(*ty),
@ -490,6 +533,7 @@ pub fn get_type_from_type_annotation_kinds(
let ty = get_type_from_type_annotation_kinds( let ty = get_type_from_type_annotation_kinds(
top_level_defs, top_level_defs,
unifier, unifier,
primitives,
ty.as_ref(), ty.as_ref(),
subst_list, subst_list,
)?; )?;
@ -499,7 +543,13 @@ pub fn get_type_from_type_annotation_kinds(
let tys = tys let tys = tys
.iter() .iter()
.map(|x| { .map(|x| {
get_type_from_type_annotation_kinds(top_level_defs, unifier, x, subst_list) get_type_from_type_annotation_kinds(
top_level_defs,
unifier,
primitives,
x,
subst_list,
)
}) })
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
Ok(unifier.add_ty(TypeEnum::TTuple { ty: tys })) Ok(unifier.add_ty(TypeEnum::TTuple { ty: tys }))

View File

@ -113,7 +113,9 @@ fn handle_typevar_definition(
x, x,
HashMap::new(), HashMap::new(),
)?; )?;
get_type_from_type_annotation_kinds(def_list, unifier, &ty, &mut None) get_type_from_type_annotation_kinds(
def_list, unifier, primitives, &ty, &mut None,
)
}) })
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
let loc = func.location; let loc = func.location;
@ -152,7 +154,7 @@ fn handle_typevar_definition(
HashMap::new(), HashMap::new(),
)?; )?;
let constraint = let constraint =
get_type_from_type_annotation_kinds(def_list, unifier, &ty, &mut None)?; get_type_from_type_annotation_kinds(def_list, unifier, primitives, &ty, &mut None)?;
let loc = func.location; let loc = func.location;
Ok(unifier.get_fresh_const_generic_var(constraint, Some(generic_name), Some(loc)).ty) Ok(unifier.get_fresh_const_generic_var(constraint, Some(generic_name), Some(loc)).ty)