Proper numeric promotion between java types for fetching the correct overload.

i.ie Math.max(20f,20) checks if the second int can be promoted to the nearest matching overload max(float, float), and fetches it
This commit is contained in:
bigguy345 committed 2025-12-29 16:12:44 +02:00
1 parent 96d12c360d
commit bc05164209
2 files changed
+124 -3

No files matched your search

@@ -119,6 +119,67 @@ public final class TypeChecker {
}
}
/**
* Check if a type is a numeric primitive (byte, short, int, long, float, double)
*/
public static boolean isNumericPrimitive(TypeInfo type) {
if (type == null || type.getJavaClass() == null) return false;
Class<?> cls = type.getJavaClass();
return cls == byte.class || cls == Byte.class ||
cls == short.class || cls == Short.class ||
cls == int.class || cls == Integer.class ||
cls == long.class || cls == Long.class ||
cls == float.class || cls == Float.class ||
cls == double.class || cls == Double.class;
}
/**
* Check if a numeric type can be promoted to a target numeric type.
* Follows Java's numeric promotion hierarchy: byte -> short -> int -> long -> float -> double
*/
public static boolean canPromoteNumeric(TypeInfo from, TypeInfo to) {
if (from == null || to == null || from.getJavaClass() == null || to.getJavaClass() == null) return false;
Class<?> fromClass = from.getJavaClass();
Class<?> toClass = to.getJavaClass();
// Unbox if necessary
if (fromClass == Byte.class) fromClass = byte.class;
if (fromClass == Short.class) fromClass = short.class;
if (fromClass == Integer.class) fromClass = int.class;
if (fromClass == Long.class) fromClass = long.class;
if (fromClass == Float.class) fromClass = float.class;
if (fromClass == Double.class) fromClass = double.class;
if (toClass == Byte.class) toClass = byte.class;
if (toClass == Short.class) toClass = short.class;
if (toClass == Integer.class) toClass = int.class;
if (toClass == Long.class) toClass = long.class;
if (toClass == Float.class) toClass = float.class;
if (toClass == Double.class) toClass = double.class;
// Get numeric ranks (higher = wider type)
int fromRank = getNumericRank(fromClass);
int toRank = getNumericRank(toClass);
return fromRank >= 0 && toRank >= 0 && fromRank <= toRank;
}
/**
* Get the numeric rank for promotion hierarchy.
* byte=0, short=1, int=2, long=3, float=4, double=5
*/
public static int getNumericRank(Class<?> cls) {
if (cls == byte.class) return 0;
if (cls == short.class) return 1;
if (cls == int.class) return 2;
if (cls == long.class) return 3;
if (cls == float.class) return 4;
if (cls == double.class) return 5;
return -1;
}
/**
* Check if the type name is a primitive or its wrapper type.
*/
@@ -321,7 +321,7 @@ public class TypeInfo {
/**
* Find the best matching method overload based on argument types.
* Uses Java's method resolution rules: exact match, then widening conversion, then autoboxing.
* Uses Java's method resolution rules: exact match, then numeric promotion, then widening conversion, then autoboxing.
*
* @param methodName The name of the method
* @param argTypes The types of the arguments being passed
@@ -359,7 +359,67 @@ public class TypeInfo {
}
}
// Phase 2: Try compatible match (widening, autoboxing, subtyping)
// Phase 2: Try numeric promotion match
// For methods with all numeric parameters, find the narrowest common type that all args can promote to
MethodInfo bestNumericMatch = null;
int bestNumericRank = Integer.MAX_VALUE;
for (MethodInfo method : overloads) {
if (method.getParameterCount() == argTypes.length) {
java.util.List<FieldInfo> params = method.getParameters();
// Check if all parameters and arguments are numeric primitives
boolean allNumeric = true;
for (int i = 0; i < argTypes.length; i++) {
TypeInfo paramType = params.get(i).getTypeInfo();
TypeInfo argType = argTypes[i];
if (paramType == null || argType == null ||
!TypeChecker.isNumericPrimitive(paramType) || !TypeChecker.isNumericPrimitive(argType)) {
allNumeric = false;
break;
}
}
if (allNumeric) {
// Check if all parameters are the same numeric type
TypeInfo commonParamType = params.get(0).getTypeInfo();
boolean allParamsSame = true;
for (int i = 1; i < params.size(); i++) {
TypeInfo paramType = params.get(i).getTypeInfo();
if (!paramType.equals(commonParamType)) {
allParamsSame = false;
break;
}
}
// If all parameters are the same numeric type, check if args can promote to it
if (allParamsSame) {
boolean canPromote = true;
for (int i = 0; i < argTypes.length; i++) {
if (!TypeChecker.canPromoteNumeric(argTypes[i], commonParamType)) {
canPromote = false;
break;
}
}
// If all args can promote, check if this is the narrowest match so far
if (canPromote) {
int paramRank = TypeChecker.getNumericRank(commonParamType.getJavaClass());
if (paramRank < bestNumericRank) {
bestNumericRank = paramRank;
bestNumericMatch = method;
}
}
}
}
}
}
if (bestNumericMatch != null) {
return bestNumericMatch;
}
// Phase 3: Try compatible match (widening, autoboxing, subtyping)
for (MethodInfo method : overloads) {
if (method.getParameterCount() == argTypes.length) {
boolean compatible = true;
@@ -376,7 +436,7 @@ public class TypeInfo {
}
}
// Phase 3: Return first overload as fallback
// Phase 4: Return first overload as fallback
return overloads.get(0);
}