Compare commits

..

4 Commits

10 changed files with 132 additions and 59 deletions

View File

@ -207,12 +207,12 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
generator: &mut dyn CodeGenerator, generator: &mut dyn CodeGenerator,
value: &Constant, value: &Constant,
ty: Type, ty: Type,
) -> BasicValueEnum<'ctx> { ) -> Option<BasicValueEnum<'ctx>> {
match value { match value {
Constant::Bool(v) => { Constant::Bool(v) => {
assert!(self.unifier.unioned(ty, self.primitives.bool)); assert!(self.unifier.unioned(ty, self.primitives.bool));
let ty = self.ctx.i8_type(); let ty = self.ctx.i8_type();
ty.const_int(if *v { 1 } else { 0 }, false).into() Some(ty.const_int(if *v { 1 } else { 0 }, false).into())
} }
Constant::Int(val) => { Constant::Int(val) => {
let ty = if self.unifier.unioned(ty, self.primitives.int32) let ty = if self.unifier.unioned(ty, self.primitives.int32)
@ -226,28 +226,33 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
} else { } else {
unreachable!(); unreachable!();
}; };
ty.const_int(*val as u64, false).into() Some(ty.const_int(*val as u64, false).into())
} }
Constant::Float(v) => { Constant::Float(v) => {
assert!(self.unifier.unioned(ty, self.primitives.float)); assert!(self.unifier.unioned(ty, self.primitives.float));
let ty = self.ctx.f64_type(); let ty = self.ctx.f64_type();
ty.const_float(*v).into() Some(ty.const_float(*v).into())
} }
Constant::Tuple(v) => { Constant::Tuple(v) => {
let ty = self.unifier.get_ty(ty); let ty = self.unifier.get_ty(ty);
let types = let types =
if let TypeEnum::TTuple { ty } = &*ty { ty.clone() } else { unreachable!() }; if let TypeEnum::TTuple { ty } = &*ty { ty.clone() } else { unreachable!() };
let values = zip(types.into_iter(), v.iter()) let values = zip(types.into_iter(), v.iter())
.map(|(ty, v)| self.gen_const(generator, v, ty)) .map_while(|(ty, v)| self.gen_const(generator, v, ty))
.collect_vec(); .collect_vec();
let types = values.iter().map(BasicValueEnum::get_type).collect_vec();
let ty = self.ctx.struct_type(&types, false); if values.len() == v.len() {
ty.const_named_struct(&values).into() let types = values.iter().map(BasicValueEnum::get_type).collect_vec();
let ty = self.ctx.struct_type(&types, false);
Some(ty.const_named_struct(&values).into())
} else {
None
}
} }
Constant::Str(v) => { Constant::Str(v) => {
assert!(self.unifier.unioned(ty, self.primitives.str)); assert!(self.unifier.unioned(ty, self.primitives.str));
if let Some(v) = self.const_strings.get(v) { if let Some(v) = self.const_strings.get(v) {
*v Some(*v)
} else { } else {
let str_ptr = let str_ptr =
self.builder.build_global_string_ptr(v, "const").as_pointer_value().into(); self.builder.build_global_string_ptr(v, "const").as_pointer_value().into();
@ -256,9 +261,22 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
let val = let val =
ty.into_struct_type().const_named_struct(&[str_ptr, size.into()]).into(); ty.into_struct_type().const_named_struct(&[str_ptr, size.into()]).into();
self.const_strings.insert(v.to_string(), val); self.const_strings.insert(v.to_string(), val);
val Some(val)
} }
} }
Constant::Ellipsis => {
let msg = self.gen_string(generator, "");
self.raise_exn(
generator,
"0:NotImplementedError",
msg,
[None, None, None],
self.current_loc,
);
None
}
_ => unreachable!(), _ => unreachable!(),
} }
} }
@ -481,7 +499,7 @@ impl<'ctx, 'a> CodeGenContext<'ctx, 'a> {
generator: &mut dyn CodeGenerator, generator: &mut dyn CodeGenerator,
s: S, s: S,
) -> BasicValueEnum<'ctx> { ) -> BasicValueEnum<'ctx> {
self.gen_const(generator, &nac3parser::ast::Constant::Str(s.into()), self.primitives.str) self.gen_const(generator, &nac3parser::ast::Constant::Str(s.into()), self.primitives.str).unwrap()
} }
pub fn raise_exn( pub fn raise_exn(
@ -1211,7 +1229,10 @@ pub fn gen_expr<'ctx, 'a, G: CodeGenerator>(
Ok(Some(match &expr.node { Ok(Some(match &expr.node {
ExprKind::Constant { value, .. } => { ExprKind::Constant { value, .. } => {
let ty = expr.custom.unwrap(); let ty = expr.custom.unwrap();
ctx.gen_const(generator, value, ty).into() let Some(const_val) = ctx.gen_const(generator, value, ty) else {
return Ok(None)
};
const_val.into()
} }
ExprKind::Name { id, .. } if id == &"none".into() => { ExprKind::Name { id, .. } if id == &"none".into() => {
match ( match (

View File

@ -604,7 +604,7 @@ pub fn exn_constructor<'ctx, 'a>(
let msg = if !args.is_empty() { let msg = if !args.is_empty() {
args.remove(0).1.to_basic_value_enum(ctx, generator, ctx.primitives.str)? args.remove(0).1.to_basic_value_enum(ctx, generator, ctx.primitives.str)?
} else { } else {
empty_string empty_string.unwrap()
}; };
ctx.builder.build_store(ptr, msg); ctx.builder.build_store(ptr, msg);
for i in [6, 7, 8].iter() { for i in [6, 7, 8].iter() {
@ -627,7 +627,7 @@ pub fn exn_constructor<'ctx, 'a>(
&[zero, int32.const_int(*i, false)], &[zero, int32.const_int(*i, false)],
"exn.str", "exn.str",
); );
ctx.builder.build_store(ptr, empty_string); ctx.builder.build_store(ptr, empty_string.unwrap());
} }
// set ints to zero // set ints to zero
for i in [2, 3].iter() { for i in [2, 3].iter() {

View File

@ -361,7 +361,7 @@ fn test_simple_function_analyze(source: Vec<&str>, tys: Vec<&str>, names: Vec<&s
pass pass
"} "}
], ],
vec!["application of type vars to generic class is not currently supported (at unknown: line 4 column 24)"]; vec!["application of type vars to generic class is not currently supported (at unknown:4:24)"];
"err no type var in generic app" "err no type var in generic app"
)] )]
#[test_case( #[test_case(
@ -417,7 +417,7 @@ fn test_simple_function_analyze(source: Vec<&str>, tys: Vec<&str>, names: Vec<&s
def __init__(): def __init__():
pass pass
"}], "}],
vec!["__init__ method must have a `self` parameter (at unknown: line 2 column 5)"]; vec!["__init__ method must have a `self` parameter (at unknown:2:5)"];
"err no self_1" "err no self_1"
)] )]
#[test_case( #[test_case(
@ -439,7 +439,7 @@ fn test_simple_function_analyze(source: Vec<&str>, tys: Vec<&str>, names: Vec<&s
"} "}
], ],
vec!["a class definition can only have at most one base class declaration and one generic declaration (at unknown: line 1 column 24)"]; vec!["a class definition can only have at most one base class declaration and one generic declaration (at unknown:1:24)"];
"err multiple inheritance" "err multiple inheritance"
)] )]
#[test_case( #[test_case(
@ -507,7 +507,7 @@ fn test_simple_function_analyze(source: Vec<&str>, tys: Vec<&str>, names: Vec<&s
pass pass
"} "}
], ],
vec!["duplicate definition of class `A` (at unknown: line 1 column 1)"]; vec!["duplicate definition of class `A` (at unknown:1:1)"];
"class same name" "class same name"
)] )]
fn test_analyze(source: Vec<&str>, res: Vec<&str>) { fn test_analyze(source: Vec<&str>, res: Vec<&str>) {

View File

@ -13,8 +13,6 @@ pub enum TypeAnnotation {
// can only be CustomClassKind // can only be CustomClassKind
Virtual(Box<TypeAnnotation>), Virtual(Box<TypeAnnotation>),
TypeVar(Type), TypeVar(Type),
/// A const generic variable.
ConstGeneric(Type),
/// A constant used in the context of a const-generic variable. /// A constant used in the context of a const-generic variable.
Constant { Constant {
/// The non-type variable associated with this constant. /// The non-type variable associated with this constant.
@ -33,7 +31,7 @@ impl TypeAnnotation {
pub fn stringify(&self, unifier: &mut Unifier) -> String { pub fn stringify(&self, unifier: &mut Unifier) -> String {
use TypeAnnotation::*; use TypeAnnotation::*;
match self { match self {
Primitive(ty) | TypeVar(ty) | ConstGeneric(ty) => unifier.stringify(*ty), Primitive(ty) | TypeVar(ty) => unifier.stringify(*ty),
CustomClass { id, params } => { CustomClass { id, params } => {
let class_name = match unifier.top_level { let class_name = match unifier.top_level {
Some(ref top) => { Some(ref top) => {
@ -329,6 +327,14 @@ pub fn parse_ast_to_type_annotation_kinds<T>(
let value = SymbolValue::from_constant(value, underlying_ty, primitives, unifier)?; let value = SymbolValue::from_constant(value, underlying_ty, primitives, unifier)?;
if matches!(value, SymbolValue::Str(_) | SymbolValue::Tuple(_) | SymbolValue::OptionSome(_)) {
return Err(format!(
"expression {} is not allowed for constant type annotation (at {})",
value.to_string(),
expr.location
))
}
Ok(TypeAnnotation::Constant { Ok(TypeAnnotation::Constant {
ty: type_var, ty: type_var,
value, value,
@ -464,17 +470,17 @@ pub fn get_type_from_type_annotation_kinds(
} }
Ok(ty) Ok(ty)
} }
TypeAnnotation::Primitive(ty) | TypeAnnotation::TypeVar(ty) | TypeAnnotation::ConstGeneric(ty) => Ok(*ty), TypeAnnotation::Primitive(ty) | TypeAnnotation::TypeVar(ty) => Ok(*ty),
TypeAnnotation::Constant { ty, value, .. } => { TypeAnnotation::Constant { ty, value, .. } => {
let ty_enum = unifier.get_ty(*ty); let ty_enum = unifier.get_ty(*ty);
let (ty, name, loc) = match &*ty_enum { let (ty, loc) = match &*ty_enum {
TypeEnum::TVar { range: ntv_underlying_ty, name, loc, is_const_generic: true, .. } => { TypeEnum::TVar { range: ntv_underlying_ty, loc, is_const_generic: true, .. } => {
(ntv_underlying_ty[0], name, loc) (ntv_underlying_ty[0], loc)
} }
_ => unreachable!("{} ({})", unifier.stringify(*ty), ty_enum.get_type_name()), _ => unreachable!("{} ({})", unifier.stringify(*ty), ty_enum.get_type_name()),
}; };
let var = unifier.get_fresh_constant(value.clone(), ty, *name, *loc); let var = unifier.get_fresh_constant(value.clone(), ty, *loc);
Ok(var) Ok(var)
} }
TypeAnnotation::Virtual(ty) => { TypeAnnotation::Virtual(ty) => {
@ -551,7 +557,7 @@ pub fn get_type_var_contained_in_type_annotation(ann: &TypeAnnotation) -> Vec<Ty
result.extend(get_type_var_contained_in_type_annotation(a)); result.extend(get_type_var_contained_in_type_annotation(a));
} }
} }
TypeAnnotation::Primitive(..) | TypeAnnotation::ConstGeneric(..) | TypeAnnotation::Constant { .. } => {} TypeAnnotation::Primitive(..) | TypeAnnotation::Constant { .. } => {}
} }
result result
} }

View File

@ -62,7 +62,7 @@ impl<'a> Inferencer<'a> {
) -> Result<(), String> { ) -> Result<(), String> {
// there are some cases where the custom field is None // there are some cases where the custom field is None
if let Some(ty) = &expr.custom { if let Some(ty) = &expr.custom {
if !self.unifier.is_concrete(*ty, &self.function_data.bound_variables) { if !matches!(&expr.node, ExprKind::Constant { value: Constant::Ellipsis, .. }) && !self.unifier.is_concrete(*ty, &self.function_data.bound_variables) {
return Err(format!( return Err(format!(
"expected concrete type at {} but got {}", "expected concrete type at {} but got {}",
expr.location, expr.location,

View File

@ -964,6 +964,7 @@ impl<'a> Inferencer<'a> {
ast::Constant::Str(_) => Ok(self.primitives.str), ast::Constant::Str(_) => Ok(self.primitives.str),
ast::Constant::None ast::Constant::None
=> report_error("CPython `None` not supported (nac3 uses `none` instead)", *loc), => report_error("CPython `None` not supported (nac3 uses `none` instead)", *loc),
ast::Constant::Ellipsis => Ok(self.unifier.get_fresh_var(None, None).0),
_ => report_error("not supported", *loc), _ => report_error("not supported", *loc),
} }
} }

View File

@ -144,7 +144,6 @@ pub enum TypeEnum {
value: SymbolValue, value: SymbolValue,
/// The underlying type of the value. /// The underlying type of the value.
ty: Type, ty: Type,
name: Option<StrRef>,
loc: Option<Location>, loc: Option<Location>,
}, },
@ -372,12 +371,11 @@ impl Unifier {
&mut self, &mut self,
value: SymbolValue, value: SymbolValue,
ty: Type, ty: Type,
name: Option<StrRef>,
loc: Option<Location>, loc: Option<Location>,
) -> Type { ) -> Type {
assert!(matches!(self.get_ty(ty).as_ref(), TypeEnum::TObj { .. })); assert!(matches!(self.get_ty(ty).as_ref(), TypeEnum::TObj { .. }));
self.add_ty(TypeEnum::TConstant { ty, value, name, loc }) self.add_ty(TypeEnum::TConstant { ty, value, loc })
} }
/// Unification would not unify rigid variables with other types, but we want to do this for /// Unification would not unify rigid variables with other types, but we want to do this for
@ -1006,8 +1004,8 @@ impl Unifier {
}; };
n n
} }
TypeEnum::TConstant { value, name, .. } => { TypeEnum::TConstant { value, .. } => {
format!("const[{}]", name.map(|n| format!("{n}={value}")).unwrap_or(value.to_string())) format!("const({value})")
} }
TypeEnum::TTuple { ty } => { TypeEnum::TTuple { ty } => {
let mut fields = let mut fields =

View File

@ -9,7 +9,7 @@ import pathlib
from numpy import int32, int64, uint32, uint64 from numpy import int32, int64, uint32, uint64
from scipy import special from scipy import special
from typing import TypeVar, Generic from typing import TypeVar, Generic, Any
T = TypeVar('T') T = TypeVar('T')
class Option(Generic[T]): class Option(Generic[T]):
@ -94,11 +94,20 @@ def patch(module):
else: else:
raise NotImplementedError raise NotImplementedError
def TypeVarDummy(zelf, name, *constraints):
if len(constraints) == 1:
zelf.__init_base__(name, *constraints, Any)
else:
zelf.__init_base__(name, *constraints)
module.int32 = int32 module.int32 = int32
module.int64 = int64 module.int64 = int64
module.uint32 = uint32 module.uint32 = uint32
module.uint64 = uint64 module.uint64 = uint64
module.TypeVar = TypeVar module.TypeVar = TypeVar
module.ConstGeneric = TypeVar
module.ConstGeneric.__init_base__ = TypeVar.__init__
module.ConstGeneric.__init__ = TypeVarDummy
module.Generic = Generic module.Generic = Generic
module.extern = extern module.extern = extern
module.Option = Option module.Option = Option

View File

@ -1,31 +1,50 @@
A = NonTypeVar("A", int32) A = ConstGeneric("A", int32)
B = NonTypeVar("B", uint32) B = ConstGeneric("B", uint32)
T = TypeVar("T")
class NTGClass(Const[A]): class ConstGenericClass(Generic[A]):
def __init__(self): def __init__(self):
pass pass
class NTG2Class(Const[A, B]): class ConstGeneric2Class(Generic[A, B]):
def __init__(self): def __init__(self):
pass pass
@extern class HybridGenericClass2(Generic[A, T]):
def make_ntg_2() -> NTGClass[2]:
...
@extern
def make_ntg2_1_2() -> NTG2Class[1, 2]:
...
def ntg(instance: NTGClass[2]):
pass pass
def ntg2(instance: NTG2Class[1, 2]): class HybridGenericClass3(Generic[T, A, B]):
pass
def make_generic_2() -> ConstGenericClass[2]:
return ...
def make_generic2_1_2() -> ConstGeneric2Class[1, 2]:
return ...
def make_hybrid_class_2_int32() -> HybridGenericClass2[2, int32]:
return ...
def make_hybrid_class_i32_0_1() -> HybridGenericClass3[int32, 0, 1]:
return ...
def consume_generic_2(instance: ConstGenericClass[2]):
pass
def consume_generic2_1_2(instance: ConstGeneric2Class[1, 2]):
pass
def consume_hybrid_class_2_i32(instance: HybridGenericClass2[2, int32]):
pass
def consume_hybrid_class_i32_0_1(instance: HybridGenericClass3[int32, 0, 1]):
pass pass
def f(): def f():
ntg(make_ntg_2()) consume_generic_2(make_generic_2())
ntg2(make_ntg2_1_2()) consume_generic2_1_2(make_generic2_1_2())
consume_hybrid_class_2_i32(make_hybrid_class_2_int32())
consume_hybrid_class_i32_0_1(make_hybrid_class_i32_0_1())
def run() -> int32: def run() -> int32:
return 0 return 0

View File

@ -25,7 +25,7 @@ use nac3core::{
}, },
}; };
use nac3parser::{ use nac3parser::{
ast::{Constant, Expr, ExprKind, StmtKind}, ast::{Constant, Expr, ExprKind, StmtKind, StrRef},
parser, parser,
}; };
@ -76,13 +76,18 @@ fn handle_typevar_definition(
) -> Result<Type, String> { ) -> Result<Type, String> {
let ExprKind::Call { func, args, .. } = &var.node else { let ExprKind::Call { func, args, .. } = &var.node else {
return Err(format!( return Err(format!(
"expression {:?} cannot be handled as a TypeVar in global scope", "expression {:?} cannot be handled as a TypeVar or ConstGeneric in global scope",
var var
)) ))
}; };
match &func.node { match &func.node {
ExprKind::Name { id, .. } if id == &"TypeVar".into() => { ExprKind::Name { id, .. } if id == &"TypeVar".into() => {
let ExprKind::Constant { value: Constant::Str(ty_name), .. } = &args[0].node else {
unreachable!("Expected string constant for first parameter of `TypeVar`, got {:?}", &args[0].node)
};
let generic_name: StrRef = ty_name.to_string().into();
let constraints = args let constraints = args
.iter() .iter()
.skip(1) .skip(1)
@ -101,11 +106,27 @@ fn handle_typevar_definition(
) )
}) })
.collect::<Result<Vec<_>, _>>()?; .collect::<Result<Vec<_>, _>>()?;
Ok(unifier.get_fresh_var_with_range(&constraints, None, None).0) let loc = func.location;
if constraints.len() == 1 {
return Err(format!("A single constraint is not allowed (at {})", loc))
}
Ok(unifier.get_fresh_var_with_range(&constraints, Some(generic_name), Some(loc)).0)
} }
ExprKind::Name { id, .. } if id == &"NonTypeVar".into() => { ExprKind::Name { id, .. } if id == &"ConstGeneric".into() => {
assert_eq!(args.len(), 2); if args.len() != 2 {
return Err(format!("Expected 2 arguments for `ConstGeneric`, got {}", args.len()))
}
let ExprKind::Constant { value: Constant::Str(ty_name), .. } = &args[0].node else {
return Err(format!(
"Expected string constant for first parameter of `ConstGeneric`, got {:?}",
&args[0].node
))
};
let generic_name: StrRef = ty_name.to_string().into();
let ty = parse_ast_to_type_annotation_kinds( let ty = parse_ast_to_type_annotation_kinds(
resolver, resolver,
@ -119,11 +140,9 @@ fn handle_typevar_definition(
let constraint = get_type_from_type_annotation_kinds( let constraint = get_type_from_type_annotation_kinds(
def_list, unifier, primitives, &ty, &mut None def_list, unifier, primitives, &ty, &mut None
)?; )?;
let ExprKind::Constant { value: Constant::Str(ty_name), .. } = &args[0].node else { let loc = func.location;
unreachable!("Expected string constant for first parameter of `NonTypeVar`, got {:?}", &args[0].node)
};
Ok(unifier.get_fresh_const_generic_var(constraint, Some(ty_name.to_string().into()), None).0) Ok(unifier.get_fresh_const_generic_var(constraint, Some(generic_name), Some(loc)).0)
} }
_ => Err(format!( _ => Err(format!(