Skip to content

Commit 76f839e

Browse files
committed
fix: enable ZynML runtime execution with multi-param functions
Key fixes for ZynML runtime execution: 1. Integer/Float Literal Types (interpreter.rs) - Changed default type for integer literals from I32 to I64 - Changed default type for float literals from F32 to F64 - Fixes type mismatch in functions returning i64 2. Parameter List Parsing (ml.zyn) - Fixed fn_params to collect ALL parameters using prepend_list - Previously only returned last parameter, breaking multi-arg calls - Also fixed: trait_params, type_params, fn_type_params, lambda_params 3. Lenient Type Checking (type_checker.rs) - Unknown variables (external functions) return fresh type var - Allows gradual typing for ZRTL builtin calls 4. Lenient Trait Registration (lib.rs) - Skip missing traits with warning instead of error - Enables compilation with partial trait resolution 5. Builtin Alias Creation (grammar.rs) - Create both alias extern (println) AND symbol extern ($IO$println) - Fixes "function not found" for builtin calls Working features: - println/print string output - Multi-argument function calls - Integer arithmetic (+, *, -, /) - Conditional statements (if) - User-defined functions with return values
1 parent 285530a commit 76f839e

15 files changed

Lines changed: 607 additions & 151 deletions

File tree

crates/compiler/src/lib.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -319,11 +319,17 @@ pub fn register_impl_blocks(program: &mut zyntax_typed_ast::TypedProgram) -> Res
319319
continue;
320320
}
321321

322-
// Find the trait by name
323-
let trait_def = program.type_registry.get_trait_by_name(impl_block.trait_name)
324-
.ok_or_else(|| {
325-
CompilerError::Analysis(format!("Trait {:?} not found in registry", impl_block.trait_name))
326-
})?;
322+
// Find the trait by name - be lenient if trait not found
323+
let trait_def = match program.type_registry.get_trait_by_name(impl_block.trait_name) {
324+
Some(t) => t,
325+
None => {
326+
// Try to resolve trait name for better warning message
327+
let trait_name_str = impl_block.trait_name.resolve_global().unwrap_or_else(|| format!("{:?}", impl_block.trait_name));
328+
log::warn!("[REGISTER_IMPL] Trait '{}' not found in registry, skipping impl block", trait_name_str);
329+
eprintln!("[REGISTER_IMPL] WARNING: Trait '{}' not found, skipping impl block", trait_name_str);
330+
continue;
331+
}
332+
};
327333
let trait_id = trait_def.id;
328334
eprintln!("[REGISTER_IMPL] Trait ID: {:?}", trait_id);
329335

crates/compiler/src/lowering.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1173,8 +1173,14 @@ impl LoweringContext {
11731173

11741174
eprintln!("[CONVERT_SIG] Return type: TypedAST = {:?}", func.return_type);
11751175
// Keep abstract types as nominal for method dispatch
1176-
let returns = vec![self.convert_type(&func.return_type)];
1177-
eprintln!("[CONVERT_SIG] Return type: HIR = {:?}", returns[0]);
1176+
// For void/unit functions, use empty returns vec (not vec![Void])
1177+
let hir_return_type = self.convert_type(&func.return_type);
1178+
let returns = if matches!(hir_return_type, HirType::Void) {
1179+
vec![] // Empty for void functions
1180+
} else {
1181+
vec![hir_return_type]
1182+
};
1183+
eprintln!("[CONVERT_SIG] Return type: HIR = {:?}", returns);
11781184

11791185
// Convert type params from TypedFunction to HirTypeParam
11801186
let hir_type_params: Vec<crate::hir::HirTypeParam> = func.type_params.iter().map(|tp| {

crates/compiler/src/ssa.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -634,19 +634,33 @@ impl SsaBuilder {
634634
}
635635
}
636636

637+
// Check if the function returns void - if so, ignore the expression value
638+
let is_void_return = matches!(
639+
&self.original_return_type,
640+
Some(Type::Primitive(zyntax_typed_ast::PrimitiveType::Unit)) | None
641+
);
642+
637643
// Check if the expression set a continuation block (for control flow expressions)
638644
if let Some(continuation) = self.continuation_block.take() {
639645
// Control flow expression - set Return on continuation block, not entry block
640646
let cont_block = self.function.blocks.get_mut(&continuation)
641647
.ok_or_else(|| crate::CompilerError::Analysis("Continuation block not found".into()))?;
642-
cont_block.terminator = HirTerminator::Return { values: vec![value_id] };
648+
cont_block.terminator = if is_void_return {
649+
HirTerminator::Return { values: vec![] }
650+
} else {
651+
HirTerminator::Return { values: vec![value_id] }
652+
};
643653

644654
// Entry block already has correct terminator (Branch/CondBranch), so return None
645655
// to signal that we shouldn't overwrite it
646656
return Ok(());
647657
} else {
648658
// Regular expression - return normally from entry block
649-
HirTerminator::Return { values: vec![value_id] }
659+
if is_void_return {
660+
HirTerminator::Return { values: vec![] }
661+
} else {
662+
HirTerminator::Return { values: vec![value_id] }
663+
}
650664
}
651665
} else {
652666
HirTerminator::Return { values: vec![] }

crates/typed_ast/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,8 +128,8 @@ pub use typed_ast::{
128128
TypedMethodCall, TypedMethod, TypedMethodParam,
129129
// Range type
130130
TypedRange,
131-
// List comprehension, slice, and import modifier types
132-
TypedListComprehension, TypedSlice, TypedImportModifier,
131+
// List comprehension, slice, import modifier, and path types
132+
TypedListComprehension, TypedSlice, TypedImportModifier, TypedPath,
133133
// Struct type
134134
TypedStructLiteral, TypedFieldInit,
135135
// Pattern types

crates/typed_ast/src/type_checker.rs

Lines changed: 44 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,7 +1175,15 @@ impl TypeChecker {
11751175
if let Some((ty, _mutability)) = self.lookup_local(*name) {
11761176
Ok(ty.clone())
11771177
} else {
1178-
Err(TypeError::UndefinedVariable(*name))
1178+
// In lenient mode, unknown variables (likely external functions)
1179+
// get a fresh type variable instead of an error.
1180+
// This supports gradual typing where external symbols may not be
1181+
// fully typed. The actual resolution happens at codegen time.
1182+
if !self.options.no_implicit_any {
1183+
Ok(self.inference.fresh_type_var())
1184+
} else {
1185+
Err(TypeError::UndefinedVariable(*name))
1186+
}
11791187
}
11801188
}
11811189

@@ -1213,6 +1221,10 @@ impl TypeChecker {
12131221
// The type is resolved to the target_type (e.g., Image, AudioBuffer)
12141222
Ok(Type::Unresolved(import.target_type))
12151223
}
1224+
TypedExpression::Path(_) => {
1225+
// Path expressions need type resolution at a later stage
1226+
Ok(self.inference.fresh_type_var())
1227+
}
12161228
}
12171229
}
12181230

@@ -1357,13 +1369,36 @@ impl TypeChecker {
13571369
has_named_params,
13581370
has_default_params,
13591371
..
1360-
} => (
1372+
} => Some((
13611373
params.clone(),
13621374
return_type.as_ref().clone(),
13631375
*has_named_params,
13641376
*has_default_params,
1365-
),
1377+
)),
1378+
// In lenient mode, unknown types (type variables, Any, Unknown) are allowed
1379+
// for function calls. We just check argument expressions and return a fresh type var.
1380+
Type::TypeVar(_) | Type::Any | Type::Unknown => {
1381+
// Check argument expressions for side effects and type consistency
1382+
for arg in &call.positional_args {
1383+
self.check_expression(&arg.node)?;
1384+
}
1385+
for arg in &call.named_args {
1386+
self.check_expression(&arg.value.node)?;
1387+
}
1388+
// Return fresh type variable since we don't know the return type
1389+
return Ok(self.inference.fresh_type_var());
1390+
}
13661391
_ => {
1392+
if !self.options.no_implicit_any {
1393+
// Lenient mode: allow unknown call types
1394+
for arg in &call.positional_args {
1395+
self.check_expression(&arg.node)?;
1396+
}
1397+
for arg in &call.named_args {
1398+
self.check_expression(&arg.value.node)?;
1399+
}
1400+
return Ok(self.inference.fresh_type_var());
1401+
}
13671402
return Err(TypeError::TypeMismatch {
13681403
expected: Type::Function {
13691404
params: vec![],
@@ -1380,6 +1415,12 @@ impl TypeChecker {
13801415
}
13811416
};
13821417

1418+
// If we don't have function type info, early return already happened above
1419+
let func_type = match func_type {
1420+
Some(ft) => ft,
1421+
None => return Ok(self.inference.fresh_type_var()),
1422+
};
1423+
13831424
let (expected_params, return_type, has_named_params, has_default_params) = func_type;
13841425

13851426
// Validate argument counts and types

crates/typed_ast/src/typed_ast.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,8 @@ pub enum TypedExpression {
524524
Slice(TypedSlice),
525525
/// Import modifier expression: import loader("path") as Type
526526
ImportModifier(TypedImportModifier),
527+
/// Path expression: Type::method or module::function
528+
Path(TypedPath),
527529
}
528530

529531
impl Default for TypedExpression {
@@ -935,6 +937,17 @@ pub struct TypedImportModifier {
935937
pub target_type: InternedString,
936938
}
937939

940+
/// Path expression: Type::method or module::function
941+
///
942+
/// Used for associated functions (static methods) and qualified paths.
943+
///
944+
/// Example: `Tensor::zeros`, `std::Option::Some`
945+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
946+
pub struct TypedPath {
947+
/// Path segments (e.g., ["Tensor", "zeros"] for Tensor::zeros)
948+
pub segments: Vec<InternedString>,
949+
}
950+
938951
/// Method call with enhanced argument support
939952
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
940953
pub struct TypedMethodCall {

0 commit comments

Comments
 (0)