public static bool TypeCompatible(VariableType dest, VariableType src, bool coerce = false) { if (dest is DynamicArrayType destArr && src is DynamicArrayType srcArr) { return(TypeCompatible(destArr.ElementType, srcArr.ElementType)); } if (dest is ClassType destClassType && src is ClassType srcClassType) { return(destClassType.ClassLimiter == srcClassType.ClassLimiter || ((Class)srcClassType.ClassLimiter).SameAsOrSubClassOf(destClassType.ClassLimiter.Name)); } if (dest.PropertyType == EPropertyType.Byte && src.PropertyType == EPropertyType.Byte) { return(true); } if (dest is DelegateType destDel && src is DelegateType srcDel) { return(true); // this seems like how it ought to be done, but there is bioware code that would have only compiled if all delegates are considered the same type // maybe log a warning here instead of an error? //return destDel.DefaultFunction.SignatureEquals(srcDel.DefaultFunction); } if (dest is Class destClass) { if (src is Class srcClass) { bool sameAsOrSubClassOf = srcClass.SameAsOrSubClassOf(destClass.Name); if (srcClass.IsInterface) { return(sameAsOrSubClassOf || destClass.Implements(srcClass)); } return(sameAsOrSubClassOf //this seems super wrong obviously. A sane type system would require an explicit downcast. //But to make this work with existing bioware code, it's this, or write a control-flow analyzer that implicitly downcasts based on typecheck conditional gates //I have chosen the lazy path || destClass.SameAsOrSubClassOf(srcClass.Name)); } if (destClass.Name.CaseInsensitiveEquals("Object") && src is ClassType) { return(true); } } if (dest.Name.CaseInsensitiveEquals(src?.Name)) { return(true); } ECast cast = CastHelper.GetConversion(dest, src); if (coerce) { return(cast != ECast.Max); } return(cast.Has(ECast.AutoConvert)); }
public static int ConversionCost(FunctionParameter dest, VariableType src) { if (dest.VarType == src) { return(0); //exact match } if (src?.PropertyType == EPropertyType.Vector && dest.VarType?.PropertyType == EPropertyType.Vector || src?.PropertyType == EPropertyType.Rotator && dest.VarType?.PropertyType == EPropertyType.Rotator) { return(0); } if (dest.VarType is Class c && (src is null || src is ClassType && !c.IsInterface)) { return(0); } if (dest.VarType is DelegateType && src is null) { return(0); } if (dest.IsOut) { return(int.MaxValue); } if (INTERFACE.CaseInsensitiveEquals(dest.VarType?.Name) && src is Class cls && cls.SameAsOrSubClassOf(INTERFACE)) { return(1); //Interface subclass } if (!INTERFACE.CaseInsensitiveEquals(dest.VarType?.Name) && dest.VarType is Class && src is Class) { return(2); } ECast conversion = GetConversion(dest.VarType, src); //if it has 'coerce', any valid conversion is acceptable, otherwise only autoconversions are acceptable if (dest.Flags.Has(UnrealFlags.EPropertyFlags.CoerceParm) ? conversion != ECast.Max : conversion.Has(ECast.AutoConvert)) { if (conversion.Has(ECast.Truncate)) { return(104); //lossy conversion } if (dest.VarType == SymbolTable.FloatType && (src == SymbolTable.IntType || src?.PropertyType == EPropertyType.Byte)) { return(103); //int to float conversion } return(101); //lossless conversion } return(int.MaxValue); }