@@ -522,6 +522,10 @@ impl SsaBuilder {
522522 // Store parameter type for SSA variable tracking
523523 self . var_types . insert ( param. name , param. ty . clone ( ) ) ;
524524
525+ // Also store typed AST type for binary op float detection
526+ let typed_ast_type = self . hir_type_to_typed_ast_type ( & param. ty ) ;
527+ self . var_typed_ast_types . insert ( param. name , typed_ast_type) ;
528+
525529 eprintln ! (
526530 "[PARAM DEBUG] Inserted into var_types: var_types['{}'] = {:?}" ,
527531 param. name. resolve_global( ) . unwrap_or_default( ) ,
@@ -1406,10 +1410,26 @@ impl SsaBuilder {
14061410 let hir_type = self . convert_type ( & let_stmt. ty ) ;
14071411 self . var_types . insert ( let_stmt. name , hir_type. clone ( ) ) ;
14081412
1409- // For TypedAST type, use initializer's type if variable type is Any
1413+ // For TypedAST type, use initializer's type if variable type is Any/Unknown
14101414 // This works around the issue where type inference doesn't update the AST
1411- let typed_ast_type = if matches ! ( let_stmt. ty, Type :: Any ) {
1412- value. ty . clone ( )
1415+ let typed_ast_type = if matches ! ( let_stmt. ty, Type :: Any | Type :: Unknown ) {
1416+ // For unary expressions, the node type may also be Unknown;
1417+ // in that case, look through to the operand's type
1418+ let init_ty = & value. ty ;
1419+ if matches ! ( init_ty, Type :: Any | Type :: Unknown ) {
1420+ if let TypedExpression :: Unary ( unary) = & value. node {
1421+ let operand_ty = self . resolve_actual_type ( & unary. operand . node , & unary. operand . ty ) ;
1422+ if !matches ! ( operand_ty, Type :: Any | Type :: Unknown ) {
1423+ operand_ty
1424+ } else {
1425+ init_ty. clone ( )
1426+ }
1427+ } else {
1428+ init_ty. clone ( )
1429+ }
1430+ } else {
1431+ init_ty. clone ( )
1432+ }
14131433 } else {
14141434 let_stmt. ty . clone ( )
14151435 } ;
@@ -2628,8 +2648,32 @@ impl SsaBuilder {
26282648 TypedExpression :: Unary ( unary) => {
26292649 let op = & unary. op ;
26302650 let operand = & unary. operand ;
2651+
2652+ // Resolve actual operand type for trait dispatch
2653+ let operand_actual_ty = self . resolve_actual_type ( & operand. node , & operand. ty ) ;
2654+
2655+ // Try trait dispatch for non-primitive types (e.g., -tensor → $Tensor$neg)
2656+ let mut operand_with_type = operand. clone ( ) ;
2657+ operand_with_type. ty = operand_actual_ty;
2658+
2659+ if let Some ( trait_call) = self . try_unary_operator_trait_dispatch (
2660+ block_id,
2661+ op,
2662+ & operand_with_type,
2663+ & expr. ty ,
2664+ ) ? {
2665+ return Ok ( trait_call) ;
2666+ }
2667+
2668+ // Regular unary operations for primitive types
26312669 let operand_val = self . translate_expression ( block_id, operand) ?;
2632- let result_type = self . convert_type ( & expr. ty ) ;
2670+ // For unary ops, result type = operand type (e.g., -int → int)
2671+ // Use operand type when expression type is Unknown/Any
2672+ let result_type = if matches ! ( expr. ty, Type :: Unknown | Type :: Any ) {
2673+ self . convert_type ( & operand_with_type. ty )
2674+ } else {
2675+ self . convert_type ( & expr. ty )
2676+ } ;
26332677
26342678 let hir_op = self . convert_unary_op ( op) ;
26352679 let result = self . create_value ( result_type. clone ( ) , HirValueKind :: Instruction ) ;
@@ -3674,20 +3718,32 @@ impl SsaBuilder {
36743718 } else if let Type :: Named { id, .. } = & receiver_type {
36753719 // Fall back to looking up in trait implementations for named types
36763720 let receiver_type_id = * id;
3721+ // Also get the type name for matching extern impls
3722+ let receiver_type_name = self . type_registry
3723+ . get_type_by_id ( receiver_type_id)
3724+ . map ( |td| td. name ) ;
36773725 let mut method_return_type = None ;
36783726 for ( _trait_id, impls) in self . type_registry . iter_implementations ( ) {
36793727 for impl_def in impls {
3680- if let Type :: Named {
3681- id : impl_type_id, ..
3682- } = & impl_def. for_type
3683- {
3684- if * impl_type_id == receiver_type_id {
3685- for method in & impl_def. methods {
3686- if method. signature . name == method_call. method {
3687- method_return_type =
3688- Some ( method. signature . return_type . clone ( ) ) ;
3689- break ;
3690- }
3728+ let impl_matches = match & impl_def. for_type {
3729+ Type :: Named { id : impl_type_id, .. } => {
3730+ * impl_type_id == receiver_type_id
3731+ }
3732+ Type :: Extern { name, .. } => {
3733+ // Extern type impls match by name
3734+ receiver_type_name. map_or ( false , |n| n == * name)
3735+ }
3736+ Type :: Unresolved ( name) => {
3737+ receiver_type_name. map_or ( false , |n| n == * name)
3738+ }
3739+ _ => false ,
3740+ } ;
3741+ if impl_matches {
3742+ for method in & impl_def. methods {
3743+ if method. signature . name == method_call. method {
3744+ method_return_type =
3745+ Some ( method. signature . return_type . clone ( ) ) ;
3746+ break ;
36913747 }
36923748 }
36933749 }
@@ -5543,6 +5599,115 @@ impl SsaBuilder {
55435599 ) )
55445600 }
55455601
5602+ /// Try to dispatch a unary operator to a trait method call.
5603+ /// Returns Some(result_value) if the operator should be dispatched via trait,
5604+ /// or None if the regular unary instruction should be used.
5605+ fn try_unary_operator_trait_dispatch (
5606+ & mut self ,
5607+ block_id : HirId ,
5608+ op : & zyntax_typed_ast:: typed_ast:: UnaryOp ,
5609+ operand : & zyntax_typed_ast:: TypedNode < zyntax_typed_ast:: typed_ast:: TypedExpression > ,
5610+ _result_ty : & Type ,
5611+ ) -> CompilerResult < Option < HirId > > {
5612+ use zyntax_typed_ast:: typed_ast:: UnaryOp as FrontendOp ;
5613+
5614+ // Only consider trait dispatch for non-primitive types
5615+ let operand_type = & operand. ty ;
5616+ if !self . is_trait_dispatchable_type ( operand_type) {
5617+ return Ok ( None ) ;
5618+ }
5619+
5620+ // Get the method and trait names for this operator
5621+ let ( method_name, trait_name) = match op {
5622+ FrontendOp :: Minus => ( "neg" , "Neg" ) ,
5623+ FrontendOp :: Not => ( "not" , "Not" ) ,
5624+ _ => return Ok ( None ) ,
5625+ } ;
5626+
5627+ // Get the type name for constructing the method symbol
5628+ let type_name = self . get_type_symbol_prefix ( operand_type) ;
5629+ if type_name. is_none ( ) {
5630+ return Ok ( None ) ;
5631+ }
5632+ let type_name = type_name. unwrap ( ) ;
5633+
5634+ // Build candidate names (same pattern as binary dispatch):
5635+ // 1) Type$method
5636+ // 2) Type$Trait$method
5637+ // 3) $Type$method (runtime symbol for extern-backed types)
5638+ let mut function_candidates = Vec :: with_capacity ( 2 ) ;
5639+ let runtime_symbol = if let Some ( stripped) = type_name. strip_prefix ( '$' ) {
5640+ function_candidates. push ( format ! ( "{}${}" , stripped, method_name) ) ;
5641+ function_candidates. push ( format ! ( "{}${}${}" , stripped, trait_name, method_name) ) ;
5642+ format ! ( "{}${}" , type_name, method_name)
5643+ } else {
5644+ function_candidates. push ( format ! ( "{}${}" , type_name, method_name) ) ;
5645+ function_candidates. push ( format ! ( "{}${}${}" , type_name, trait_name, method_name) ) ;
5646+ format ! ( "${}${}" , type_name, method_name)
5647+ } ;
5648+
5649+ let mut matched_function: Option < ( HirId , String ) > = None ;
5650+ for candidate in & function_candidates {
5651+ let candidate_interned = InternedString :: new_global ( candidate) ;
5652+ if let Some ( & func_id) = self . function_symbols . get ( & candidate_interned) {
5653+ matched_function = Some ( ( func_id, candidate. clone ( ) ) ) ;
5654+ break ;
5655+ }
5656+ }
5657+
5658+ let callee = if let Some ( ( func_id, matched_name) ) = matched_function {
5659+ log:: debug!(
5660+ "[SSA] Unary trait dispatch: using function '{}' for type {}" ,
5661+ matched_name,
5662+ type_name
5663+ ) ;
5664+ crate :: hir:: HirCallable :: Function ( func_id)
5665+ } else if type_name. starts_with ( '$' ) {
5666+ log:: debug!(
5667+ "[SSA] Unary trait dispatch: using runtime symbol '{}' for type {}" ,
5668+ runtime_symbol,
5669+ type_name
5670+ ) ;
5671+ crate :: hir:: HirCallable :: Symbol ( runtime_symbol)
5672+ } else {
5673+ return Ok ( None ) ;
5674+ } ;
5675+
5676+ // Translate the operand
5677+ let operand_val = self . translate_expression ( block_id, operand) ?;
5678+
5679+ // Result type should be the same as the operand type (e.g., -Tensor = Tensor)
5680+ let hir_result_type = self . convert_type ( operand_type) ;
5681+ log:: debug!(
5682+ "[SSA] Unary trait dispatch result type: {:?} (from operand type: {:?})" ,
5683+ hir_result_type,
5684+ operand_type
5685+ ) ;
5686+
5687+ // Create call instruction to the trait method
5688+ let result = if hir_result_type != HirType :: Void {
5689+ Some ( self . create_value ( hir_result_type. clone ( ) , HirValueKind :: Instruction ) )
5690+ } else {
5691+ None
5692+ } ;
5693+
5694+ let inst = HirInstruction :: Call {
5695+ result,
5696+ callee,
5697+ args : vec ! [ operand_val] ,
5698+ type_args : vec ! [ ] ,
5699+ const_args : vec ! [ ] ,
5700+ is_tail : false ,
5701+ } ;
5702+
5703+ self . add_instruction ( block_id, inst) ;
5704+ self . add_use ( operand_val, result. unwrap_or ( operand_val) ) ;
5705+
5706+ Ok ( Some (
5707+ result. unwrap_or_else ( || self . create_undef ( HirType :: Void ) ) ,
5708+ ) )
5709+ }
5710+
55465711 /// Resolve the actual type for an expression, looking up variable types if needed.
55475712 /// This is needed because expression nodes may have type `Any` even when the
55485713 /// variable has a more specific type recorded in var_typed_ast_types.
0 commit comments