Repository: pola-rs/polars
File: crates/polars-plan/src/plans/conversion/type_coercion/is_in.rs (Rust)

Code before:
                &type_left, &type_other
            );
            return Ok(None);
        },

        // don't attempt to cast between obviously mismatched types, but
        // allow integer/float comparison (will use their supertypes).
        (a, b) => {
            if (a.is_primitive_numeric() && b.is_primitive_numeric()) || (a == &DataType::Null) {
                if a != b {
                    // @TAG: 2.0
                    // @HACK: `is_in` does supertype casting between primitive numerics, which
                    // honestly makes very little sense. To stay backwards compatible we keep this,
                    // but please in 2.0 remove this. FirstArgLossless might be a good alternative,
                    // as used by index_of(), or build on index_of().
                    //
                    // For integers, use lossless supertype to avoid precision loss (e.g.,
                    // UInt64 + Int64 would otherwise become Float64). When no lossless supertype
                    // exists (e.g., UInt128 vs Int64), fall back to the series type.
                    let super_type =
                        if !a.is_unknown() && !b.is_unknown() && a.is_integer() && b.is_integer() {
                            polars_core::utils::get_numeric_upcast_supertype_lossless(
                                &type_left,
                                type_other_inner,
                            )
                            .unwrap_or_else(|| type_left.clone())
                        } else {
                            polars_core::utils::try_get_supertype(&type_left, type_other_inner)?
                        };

                    let other_type = match &type_other {
                        DataType::List(_) => DataType::List(Box::new(super_type.clone())),
                        #[cfg(feature = "dtype-array")]
                        DataType::Array(_, width) => {
                            DataType::Array(Box::new(super_type.clone()), *width)
                        },
                        _ => unreachable!(),
                    };

                    return Ok(Some(IsInTypeCoercionResult::SuperType(
                        super_type, other_type,
                    )));
                }

                return Ok(None);
            }
            polars_bail!(InvalidOperation: "'{op}' cannot check for {:?} values in {:?} data", &type_other, &type_left)
        },
    };
    Ok(Some(casted_expr))
}

Review comment (suggestion):
I don't think just taking the LHS type is correct here.

For this PR I believe we just need to add a call to `get_numeric_upcast_supertype_lossless()`, and then fallback to the existing `try_get_supertype()` if that doesn't work


Code after:
                &type_left, &type_other
            );
            return Ok(None);
        },

        // don't attempt to cast between obviously mismatched types, but
        // allow integer/float comparison (will use their supertypes).
        (a, b) => {
            if (a.is_primitive_numeric() && b.is_primitive_numeric()) || (a == &DataType::Null) {
                if a != b {
                    // @TAG: 2.0
                    // @HACK: `is_in` does supertype casting between primitive numerics, which
                    // honestly makes very little sense. To stay backwards compatible we keep this,
                    // but please in 2.0 remove this. FirstArgLossless might be a good alternative,
                    // as used by index_of(), or build on index_of().
                    //
                    // Try lossless supertype first to avoid precision loss (e.g.,
                    // UInt64 + Int64 would otherwise become Float64), then fall back
                    // to the existing supertype behavior.
                    let super_type = polars_core::utils::get_numeric_upcast_supertype_lossless(
                        &type_left,
                        type_other_inner,
                    )
                    .map_or_else(
                        || polars_core::utils::try_get_supertype(&type_left, type_other_inner),
                        Ok,
                    )?;

                    let other_type = match &type_other {
                        DataType::List(_) => DataType::List(Box::new(super_type.clone())),
                        #[cfg(feature = "dtype-array")]
                        DataType::Array(_, width) => {
                            DataType::Array(Box::new(super_type.clone()), *width)
                        },
                        _ => unreachable!(),
                    };

                    return Ok(Some(IsInTypeCoercionResult::SuperType(
                        super_type, other_type,
                    )));
                }

                return Ok(None);
            }
            polars_bail!(InvalidOperation: "'{op}' cannot check for {:?} values in {:?} data", &type_other, &type_left)
        },
    };
    Ok(Some(casted_expr))
}