@@ -3367,6 +3367,21 @@ fn try_lower_comparison(
33673367 . collect ( ) ;
33683368 ( name. clone ( ) , args, Some ( module_source. clone ( ) ) )
33693369 }
3370+ ResolvedType :: Tuple ( elems) => {
3371+ // Tuple comparison: expand to element-wise comparisons inline.
3372+ // [i32, String] == [i32, String] → a.0 == b.0 && a.1 == b.1
3373+ let elems = elems. clone ( ) ;
3374+ return lower_tuple_comparison (
3375+ trait_method_locations,
3376+ current_module_source,
3377+ span,
3378+ op,
3379+ left,
3380+ right,
3381+ & elems,
3382+ type_table,
3383+ ) ;
3384+ }
33703385 _ => return None ,
33713386 } ;
33723387
@@ -3482,6 +3497,113 @@ fn try_lower_comparison(
34823497 None
34833498}
34843499
3500+ /// Expand a tuple comparison to element-wise comparisons.
3501+ ///
3502+ /// For `Eq`/`NotEq`: `a == b` → `a.0 == b.0 && a.1 == b.1 && ...`
3503+ /// For `Ord` comparisons: `a < b` → `Tuple^Ord::cmp` (delegates to element-wise cmp)
3504+ ///
3505+ /// Each element comparison is recursively lowered: struct elements become
3506+ /// `Eq::eq` method calls, primitives remain as binary ops.
3507+ fn lower_tuple_comparison (
3508+ trait_method_locations : & IndexMap < String , ModuleSource > ,
3509+ current_module_source : & ModuleSource ,
3510+ span : crate :: token:: Span ,
3511+ op : TirBinaryOp ,
3512+ left : & TirExpr ,
3513+ right : & TirExpr ,
3514+ elems : & [ TypeId ] ,
3515+ type_table : & mut TypeTable ,
3516+ ) -> Option < TirExprKind > {
3517+ if !matches ! ( op, TirBinaryOp :: Eq | TirBinaryOp :: NotEq ) {
3518+ // Ord comparisons on tuples are not yet supported
3519+ return None ;
3520+ }
3521+
3522+ if elems. is_empty ( ) {
3523+ // Empty tuple: always equal
3524+ let result = TirExprKind :: BoolLiteral ( true ) ;
3525+ if op == TirBinaryOp :: NotEq {
3526+ return Some ( TirExprKind :: Unary {
3527+ op : TirUnaryOp :: Not ,
3528+ expr : Box :: new ( TirExpr :: new ( result, TypeTable :: BOOL , span) ) ,
3529+ } ) ;
3530+ }
3531+ return Some ( result) ;
3532+ }
3533+
3534+ // Build element-wise equality: a.0 == b.0 && a.1 == b.1 && ...
3535+ let mut combined: Option < TirExpr > = None ;
3536+
3537+ for ( i, & elem_type) in elems. iter ( ) . enumerate ( ) {
3538+ let left_field = TirExpr :: new (
3539+ TirExprKind :: FieldAccess {
3540+ expr : Box :: new ( left. clone ( ) ) ,
3541+ field_index : i as u32 ,
3542+ field_name : i. to_string ( ) ,
3543+ } ,
3544+ elem_type,
3545+ span,
3546+ ) ;
3547+ let right_field = TirExpr :: new (
3548+ TirExprKind :: FieldAccess {
3549+ expr : Box :: new ( right. clone ( ) ) ,
3550+ field_index : i as u32 ,
3551+ field_name : i. to_string ( ) ,
3552+ } ,
3553+ elem_type,
3554+ span,
3555+ ) ;
3556+
3557+ // Try to lower this element comparison (e.g., struct → MethodCall).
3558+ // Use an empty trait_method_locations — the element's own try_lower_comparison
3559+ // will handle struct/variant/generic types. For primitives, it returns None
3560+ // and we use a direct Binary op.
3561+ let elem_eq = if let Some ( lowered) = try_lower_comparison (
3562+ trait_method_locations,
3563+ current_module_source,
3564+ span,
3565+ TirBinaryOp :: Eq ,
3566+ & left_field,
3567+ & right_field,
3568+ type_table,
3569+ ) {
3570+ TirExpr :: new ( lowered, TypeTable :: BOOL , span)
3571+ } else {
3572+ TirExpr :: new (
3573+ TirExprKind :: Binary {
3574+ op : TirBinaryOp :: Eq ,
3575+ left : Box :: new ( left_field) ,
3576+ right : Box :: new ( right_field) ,
3577+ } ,
3578+ TypeTable :: BOOL ,
3579+ span,
3580+ )
3581+ } ;
3582+
3583+ combined = Some ( match combined {
3584+ None => elem_eq,
3585+ Some ( prev) => TirExpr :: new (
3586+ TirExprKind :: Binary {
3587+ op : TirBinaryOp :: And ,
3588+ left : Box :: new ( prev) ,
3589+ right : Box :: new ( elem_eq) ,
3590+ } ,
3591+ TypeTable :: BOOL ,
3592+ span,
3593+ ) ,
3594+ } ) ;
3595+ }
3596+
3597+ let result = combined. unwrap ( ) ;
3598+ if op == TirBinaryOp :: NotEq {
3599+ return Some ( TirExprKind :: Unary {
3600+ op : TirUnaryOp :: Not ,
3601+ expr : Box :: new ( result) ,
3602+ } ) ;
3603+ }
3604+ Some ( result. kind )
3605+ }
3606+
34853607/// Convert a trait method name to a TIR binary operator, if applicable.
34863608/// Used when a type-param-receiver method call is monomorphized to a primitive type.
34873609fn trait_method_to_binary_op ( trait_name : Option < & str > , method_name : & str ) -> Option < TirBinaryOp > {
0 commit comments