1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-08 03:28:20 +02:00

Fixed a broken test

This commit is contained in:
Brian Fiete 2019-11-19 10:31:53 -08:00
parent 098ad1ce55
commit 503261e916
3 changed files with 10 additions and 936 deletions

View file

@ -6648,11 +6648,11 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
bool implementsInterface = false;
if (origCheckArgType != checkArgType)
{
implementsInterface = CanImplicitlyCast(origCheckArgType, convCheckConstraint);
implementsInterface = CanImplicitlyCast(BfTypedValue(BfIRValue::sValueless, origCheckArgType), convCheckConstraint);
}
if (!implementsInterface)
implementsInterface = CanImplicitlyCast(checkArgType, convCheckConstraint);
implementsInterface = CanImplicitlyCast(BfTypedValue(BfIRValue::sValueless, checkArgType), convCheckConstraint);
if ((!implementsInterface) && (origCheckArgType->IsWrappableType()))
{

View file

@ -7694,941 +7694,15 @@ BfType* BfModule::ResolveTypeRef(BfAstNode* astNode, const BfSizedArray<BfTypeRe
return ResolveTypeRef(typeRef, populateType, resolveFlags);
}
bool BfModule::DoCanImplicitlyCast(BfTypedValue typedVal, BfType* toType, BfCastFlags castFlags)
{
BfType* fromType = typedVal.mType;
if (fromType == toType)
return true;
// Ref X to Ref Y, X* to Y*
{
bool checkUnderlying = false;
if (((typedVal.mType->IsRef()) && (toType->IsRef())))
{
auto fromRefType = (BfRefType*)typedVal.mType;
auto toRefType = (BfRefType*)toType;
if (fromRefType->mRefKind == toRefType->mRefKind)
checkUnderlying = true;
else if ((fromRefType->mRefKind == BfRefType::RefKind_Ref) && (toRefType->mRefKind == BfRefType::RefKind_Mut))
checkUnderlying = true; // Allow a ref-to-mut implicit conversion
}
if ((typedVal.mType->IsPointer()) && (toType->IsPointer()))
checkUnderlying = true;
if (checkUnderlying)
{
auto fromInner = typedVal.mType->GetUnderlyingType();
auto toInner = toType->GetUnderlyingType();
if (fromInner == toInner)
return true;
// ref int <-> ref int64/int32 (of same size)
if (((fromInner->IsInteger()) && (toInner->IsInteger())) &&
(fromInner->mSize == toInner->mSize) &&
(fromInner->IsSigned() == toInner->IsSigned()))
return true;
}
}
// Generic param -> *
if ((typedVal.mType->IsGenericParam()) && (!toType->IsGenericParam()))
{
if (toType == mContext->mBfObjectType)
{
//auto resolvedType = ResolveGenericType(typedVal.mType);
//auto resolvedType = typedVal.mType;
/*if (!resolvedType->IsGenericParam())
return CanImplicitlyCast(BfTypedValue(typedVal.mValue, resolvedType), toType);*/
// Can we never NOT do this?
return true;
}
// For these casts, it's just important we get *A* value to work with here,
// as this is just use for unspecialized parsing. We don't use the generated code
auto genericParamInst = GetGenericParamInstance((BfGenericParamType*)typedVal.mType);
if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Var) != 0)
{
return true;
}
if (toType->IsInterface())
{
for (auto iface : genericParamInst->mInterfaceConstraints)
if (TypeIsSubTypeOf(iface, toType->ToTypeInstance()))
return true;
}
if (genericParamInst->mTypeConstraint != NULL)
{
auto defaultFromValue = GetDefaultTypedValue(genericParamInst->mTypeConstraint);
auto result = CanImplicitlyCast(defaultFromValue, toType);
if ((result) && (genericParamInst->mTypeConstraint->IsDelegate()) && (toType->IsDelegate()))
{
// Don't allow cast when we are constrained by a delegate type, because BfMethodRefs can match and we require an actual alloc
return false;
}
return result;
}
// Generic constrained with class or pointer type -> void*
if (toType->IsVoidPtr())
{
if ((genericParamInst->mGenericParamFlags & (BfGenericParamFlag_Class | BfGenericParamFlag_StructPtr)) ||
((genericParamInst->mTypeConstraint != NULL) &&
((genericParamInst->mTypeConstraint->IsPointer()) || (genericParamInst->mTypeConstraint->IsObjectOrInterface()))))
{
return true;
}
}
}
// * -> Generic param
if (toType->IsGenericParam())
{
auto genericParamInst = GetGenericParamInstance((BfGenericParamType*)toType);
if (genericParamInst->mGenericParamFlags & BfGenericParamFlag_Var)
return true;
if (typedVal.mType->IsNull())
{
bool allowCast = (genericParamInst->mGenericParamFlags & BfGenericParamFlag_Class) || (genericParamInst->mGenericParamFlags & BfGenericParamFlag_StructPtr);
if ((!allowCast) && (genericParamInst->mTypeConstraint != NULL))
allowCast = genericParamInst->mTypeConstraint->IsObject() || genericParamInst->mTypeConstraint->IsPointer();
if (allowCast)
return true;
}
if (genericParamInst->mTypeConstraint != NULL)
{
if (CanImplicitlyCast(typedVal, genericParamInst->mTypeConstraint))
return true;
}
}
// Struct truncate
if ((fromType->IsStruct()) && (toType->IsStruct()))
{
auto fromTypeInstance = fromType->ToTypeInstance();
auto toTypeInstance = toType->ToTypeInstance();
if (TypeIsSubTypeOf(fromTypeInstance, toTypeInstance))
return true;
}
if ((fromType->IsVar()) || (toType->IsVar()))
return true;
// Null -> ObjectInst|IFace|ptr
if ((fromType->IsNull()) &&
((toType->IsObjectOrInterface()) || (toType->IsPointer()) || (toType->IsPointer())))
{
return true;
}
// Object/struct -> object/struct|IFace
if ((fromType->IsTypeInstance()) && ((toType->IsTypeInstance() || (toType->IsInterface()))))
{
auto fromTypeInstance = fromType->ToTypeInstance();
auto toTypeInstance = toType->ToTypeInstance();
if (TypeIsSubTypeOf(fromTypeInstance, toTypeInstance))
return true;
}
// concrete IFace -> object|IFace
if ((fromType->IsConcreteInterfaceType()) && ((toType->IsObject() || (toType->IsInterface()))))
{
auto concreteInterfaceType = (BfConcreteInterfaceType*)fromType;
if ((toType->IsObject()) || (concreteInterfaceType->mInterface == toType))
return true;
}
// IFace -> object
if ((fromType->IsInterface()) && (toType == mContext->mBfObjectType))
return true;
if (toType->IsPointer())
{
// Ptr -> Ptr
if (fromType->IsPointer())
{
bool allowCast = false;
auto fromPointerType = (BfPointerType*)typedVal.mType;
auto toPointerType = (BfPointerType*)toType;
auto fromUnderlying = fromPointerType->mElementType;
auto toUnderlying = toPointerType->mElementType;
// Allow cast from T[size]* to T* implicitly
// And from T* to T[size]* explicitly
if (fromUnderlying->IsSizedArray())
fromUnderlying = fromUnderlying->GetUnderlyingType();
// if ((toUnderlying->IsSizedArray()) && (explicitCast))
// toUnderlying = toUnderlying->GetUnderlyingType();
if ((fromUnderlying == toUnderlying) ||
(TypeIsSubTypeOf(fromUnderlying->ToTypeInstance(), toUnderlying->ToTypeInstance())) ||
(toUnderlying->IsVoid()))
allowCast = true;
if (allowCast)
return true;
}
else if (fromType->IsObject())
{
auto fromTypeInst = fromType->ToTypeInstance();
auto charType = GetPrimitiveType(BfTypeCode_Char8);
auto charPtrType = CreatePointerType(charType);
if ((fromTypeInst->mTypeDef == mCompiler->mStringTypeDef) && (toType == charPtrType))
{
// String Object -> char* literal
return true;
}
}
/*else if (typedVal.mType->IsSizedArray())
{
if (typedVal.IsAddr())
{
BfSizedArrayType* arrayType = (BfSizedArrayType*)typedVal.mType;
BfTypedValue returnPointer(typedVal.mValue, CreatePointerType(arrayType->mElementType));
return CanImplicitlyCast(returnPointer, toType);
}
}*/
}
// Boxing?
if (((fromType->IsValueType()) || (fromType->IsPointer()) || (fromType->IsValuelessType())) &&
((toType->IsInterface()) || (toType == mContext->mBfObjectType)))
{
// if (fromType->IsPointer())
// {
// if (toType == mContext->mBfObjectType)
// return true;
// return false;
// }
if (toType == mContext->mBfObjectType)
return true;
BfTypeInstance* fromStructTypeInstance = NULL;
fromStructTypeInstance = fromType->ToTypeInstance();
if (fromStructTypeInstance == NULL)
{
if (fromType->IsPrimitiveType())
{
auto primType = (BfPrimitiveType*)fromType;
fromStructTypeInstance = GetPrimitiveStructType(primType->mTypeDef->mTypeCode);
}
else
return false;
}
auto toTypeInstance = toType->ToTypeInstance();
// Need to box it
if (TypeIsSubTypeOf(fromStructTypeInstance, toTypeInstance))
return true;
}
// Null -> Nullable<T>
if ((typedVal.mType->IsNull()) && (toType->IsNullable()))
{
return true;
}
// Nullable<A> -> Nullable<B>
if ((typedVal.mType->IsNullable()) && (toType->IsNullable()))
{
auto fromNullableType = (BfGenericTypeInstance*)typedVal.mType;
auto toNullableType = (BfGenericTypeInstance*)toType;
return CanImplicitlyCast(BfTypedValue(mBfIRBuilder->GetFakeVal(), fromNullableType->mTypeGenericArguments[0]), toNullableType->mTypeGenericArguments[0], castFlags);
}
// Tuple -> Tuple
if ((typedVal.mType->IsTuple()) && (toType->IsTuple()))
{
auto fromTupleType = (BfTupleType*)typedVal.mType;
auto toTupleType = (BfTupleType*)toType;
if (fromTupleType->mFieldInstances.size() == toTupleType->mFieldInstances.size())
{
bool canCast = true;
BfIRValue curTupleValue = mBfIRBuilder->CreateUndefValue(mBfIRBuilder->MapType(toTupleType));
for (int valueIdx = 0; valueIdx < (int)fromTupleType->mFieldInstances.size(); valueIdx++)
{
BfFieldInstance* fromFieldInstance = &fromTupleType->mFieldInstances[valueIdx];
BfFieldInstance* toFieldInstance = &toTupleType->mFieldInstances[valueIdx];
//
{
BfFieldDef* fromFieldDef = fromFieldInstance->GetFieldDef();
BfFieldDef* toFieldDef = toFieldInstance->GetFieldDef();
// Either the names have to match or one has to be unnamed
if ((!fromFieldDef->IsUnnamedTupleField()) && (!toFieldDef->IsUnnamedTupleField()) &&
(fromFieldDef->mName != toFieldDef->mName))
{
curTupleValue = BfIRValue();
break;
}
}
auto fromFieldType = fromFieldInstance->GetResolvedType();
auto toFieldType = toFieldInstance->GetResolvedType();
if (toFieldType->IsVoid())
continue; // Allow sinking to void
BfIRValue fromFieldValue;
bool canCastField = CanImplicitlyCast(BfTypedValue(mBfIRBuilder->GetFakeVal(), fromFieldType), toFieldType, (BfCastFlags)(castFlags | BfCastFlags_Explicit));
if (!canCastField)
{
canCast = false;
break;
}
}
if (canCast)
return false;
}
}
/*if (fromType->IsRef())
{
if (toType->IsRef())
{
BfTypedValue unrefValue = BfTypedValue(typedVal.mValue, fromType->GetUnderlyingType(), true);
return CanImplicitlyCast(unrefValue, toType->GetUnderlyingType());
}
else
{
// ref T -> T
return fromType->GetUnderlyingType() == toType;
}
}*/
// Int -> Enum
if ((typedVal.mType->IsIntegral()) && (toType->IsEnum()))
{
// Allow implicit cast of zero
if (typedVal.mValue)
{
auto constant = mBfIRBuilder->GetConstant(typedVal.mValue);
if ((constant != NULL) && (BfIRBuilder::IsInt(constant->mTypeCode)))
{
int64 srcVal = constant->mInt64;
if (srcVal == 0)
return true;
}
}
}
// Tuple -> Tuple
if ((typedVal.mType->IsTuple()) && (toType->IsTuple()))
{
auto fromTupleType = (BfTupleType*)typedVal.mType;
auto toTupleType = (BfTupleType*)toType;
if (fromTupleType->mFieldInstances.size() == toTupleType->mFieldInstances.size())
{
for (int valueIdx = 0; valueIdx < (int)fromTupleType->mFieldInstances.size(); valueIdx++)
{
BfFieldInstance* fromFieldInstance = &fromTupleType->mFieldInstances[valueIdx];
BfFieldInstance* toFieldInstance = &toTupleType->mFieldInstances[valueIdx];
BfFieldDef* fromFieldDef = fromFieldInstance->GetFieldDef();
BfFieldDef* toFieldDef = toFieldInstance->GetFieldDef();
auto fromFieldType = fromFieldInstance->GetResolvedType();
auto toFieldType = toFieldInstance->GetResolvedType();
// Either the names have to match or one has to be unnamed
if ((!fromFieldDef->IsUnnamedTupleField()) && (!toFieldDef->IsUnnamedTupleField()) &&
(fromFieldDef->mName != toFieldDef->mName))
return false;
if (toFieldType->IsVoid())
continue; // Allow sinking to void
if (!CanImplicitlyCast(GetFakeTypedValue(fromFieldType), toFieldType))
return false;
}
return true;
}
}
// -> const <value>
if (toType->IsConstExprValue())
{
auto constant = mBfIRBuilder->GetConstant(typedVal.mValue);
if (constant != NULL)
{
BfConstExprValueType* toConstExprValueType = (BfConstExprValueType*)toType;
auto variantVal = TypedValueToVariant(NULL, typedVal);
if ((mBfIRBuilder->IsInt(variantVal.mTypeCode)) && (mBfIRBuilder->IsInt(toConstExprValueType->mValue.mTypeCode)))
{
if (variantVal.mInt64 == toConstExprValueType->mValue.mInt64)
return true;
}
else if ((mBfIRBuilder->IsFloat(variantVal.mTypeCode)) && (mBfIRBuilder->IsFloat(toConstExprValueType->mValue.mTypeCode)))
{
if (variantVal.ToDouble() == toConstExprValueType->mValue.ToDouble())
return true;
}
}
}
if ((fromType->IsPrimitiveType()) && (toType->IsPrimitiveType()))
{
auto fromPrimType = (BfPrimitiveType*)fromType;
auto toPrimType = (BfPrimitiveType*)toType;
BfTypeCode fromTypeCode = fromPrimType->mTypeDef->mTypeCode;
BfTypeCode toTypeCode = toPrimType->mTypeDef->mTypeCode;
// Must be from a default int to do an implicit constant cast, not casted by user, ie: (ushort)123
if ((toType->IsIntegral()) && (typedVal.mValue))
{
// Allow constant ints to be implicitly casted to a smaller type if they fit
auto constant = mBfIRBuilder->GetConstant(typedVal.mValue);
if (constant != NULL)
{
if (BfIRBuilder::IsInt(constant->mTypeCode))
{
int64 srcVal = constant->mInt64;
if (toPrimType->IsChar())
{
if (srcVal == 0)
return true;
}
else if ((fromPrimType->IsChar()) && (!toPrimType->IsChar()))
{
// Never allow this
}
else if ((constant->mTypeCode == BfTypeCode_UInt64) && (srcVal < 0))
{
// There's nothing that this could fit into
}
else if (toType->IsSigned())
{
if (toType->mSize == 8) // int64
return true;
else
{
int64 minVal = -(1LL << (8 * toType->mSize - 1));
int64 maxVal = (1LL << (8 * toType->mSize - 1)) - 1;
if ((srcVal >= minVal) && (srcVal <= maxVal))
return true;
}
}
else if (toType->mSize == 8) // ulong
{
if (srcVal >= 0)
return true;
}
else
{
int64 minVal = 0;
int64 maxVal = (1LL << (8 * toType->mSize)) - 1;
if ((srcVal >= minVal) && (srcVal <= maxVal))
return true;
}
}
else if (constant->mConstType == BfConstType_Undef)
{
BF_ASSERT(mBfIRBuilder->mIgnoreWrites);
auto undefConst = (BfConstantUndef*)constant;
auto fakeVal = GetFakeTypedValue(GetPrimitiveType(undefConst->mTypeCode));
if (CanImplicitlyCast(fakeVal, toType))
return true;
}
}
}
switch (toTypeCode)
{
case BfTypeCode_UInt8:
switch (fromTypeCode)
{
case BfTypeCode_UInt8:
return true;
default: break;
}
break;
case BfTypeCode_Char16:
switch (fromTypeCode)
{
case BfTypeCode_Char8:
return true;
default: break;
}
break;
case BfTypeCode_Int16:
switch (fromTypeCode)
{
case BfTypeCode_Int8:
return true;
case BfTypeCode_UInt8:
return true;
default: break;
}
break;
case BfTypeCode_UInt16:
switch (fromTypeCode)
{
case BfTypeCode_UInt8:
return true;
default: break;
}
break;
case BfTypeCode_Int32:
switch (fromTypeCode)
{
case BfTypeCode_Int8:
case BfTypeCode_Int16:
return true;
case BfTypeCode_IntPtr:
if (mCompiler->mSystem->mPtrSize == 4)
return true;
break;
case BfTypeCode_UInt8:
case BfTypeCode_UInt16:
return true;
default: break;
}
break;
case BfTypeCode_Char32:
switch (fromTypeCode)
{
case BfTypeCode_Char8:
case BfTypeCode_Char16:
return true;
default: break;
}
break;
case BfTypeCode_UInt32:
switch (fromTypeCode)
{
case BfTypeCode_UInt8:
case BfTypeCode_UInt16:
case BfTypeCode_UInt32:
return true;
case BfTypeCode_UIntPtr:
if (mCompiler->mSystem->mPtrSize == 4)
return true;
break;
default: break;
}
break;
case BfTypeCode_Int64:
switch (fromTypeCode)
{
case BfTypeCode_Int8:
case BfTypeCode_Int16:
case BfTypeCode_Int32:
case BfTypeCode_IntPtr:
return true;
case BfTypeCode_UInt8:
case BfTypeCode_UInt16:
case BfTypeCode_UInt32:
return true;
default: break;
}
break;
case BfTypeCode_UInt64:
switch (fromTypeCode)
{
case BfTypeCode_UInt8:
case BfTypeCode_UInt16:
case BfTypeCode_UInt32:
case BfTypeCode_UIntPtr:
return true;
default: break;
}
break;
case BfTypeCode_IntPtr:
switch (fromTypeCode)
{
case BfTypeCode_Int8:
case BfTypeCode_Int16:
case BfTypeCode_Int32:
return true;
case BfTypeCode_UInt8:
case BfTypeCode_UInt16:
return true;
case BfTypeCode_UInt32:
case BfTypeCode_Int64:
if (mCompiler->mSystem->mPtrSize == 8)
return true;
break;
default: break;
}
break;
case BfTypeCode_UIntPtr:
switch (fromTypeCode)
{
case BfTypeCode_UInt8:
case BfTypeCode_UInt16:
case BfTypeCode_UInt32:
return true;
case BfTypeCode_UInt64:
if (mCompiler->mSystem->mPtrSize == 8)
return true;
break;
default: break;
}
break;
case BfTypeCode_Single:
switch (fromTypeCode)
{
case BfTypeCode_Int8:
case BfTypeCode_Int16:
case BfTypeCode_Int32:
case BfTypeCode_Int64:
case BfTypeCode_IntPtr:
case BfTypeCode_IntUnknown:
return true;
case BfTypeCode_UInt8:
case BfTypeCode_UInt16:
case BfTypeCode_UInt32:
case BfTypeCode_UInt64:
case BfTypeCode_UIntPtr:
case BfTypeCode_UIntUnknown:
return true;
default: break;
}
break;
case BfTypeCode_Double:
switch (fromTypeCode)
{
case BfTypeCode_Int8:
case BfTypeCode_Int16:
case BfTypeCode_Int32:
case BfTypeCode_Int64:
case BfTypeCode_IntPtr:
case BfTypeCode_IntUnknown:
return true;
case BfTypeCode_UInt8:
case BfTypeCode_UInt16:
case BfTypeCode_UInt32:
case BfTypeCode_UInt64:
case BfTypeCode_UIntPtr:
case BfTypeCode_UIntUnknown:
return true;
case BfTypeCode_Single:
return true;
default: break;
}
break;
default: break;
}
}
// wrappable -> struct
// if ((fromType->IsWrappableType()) && (toType->IsStruct()))
// {
// auto wrappableType = GetWrappedStructType(fromType);
// if (TypeIsSubTypeOf(wrappableType, toType->ToTypeInstance()))
// return true;
// }
// Check user-defined operators
// {
// auto fromTypeInstance = fromType->ToTypeInstance();
// auto toTypeInstance = toType->ToTypeInstance();
//
// int bestFromDist = INT_MAX;
// BfType* bestFromType = NULL;
// int bestNegFromDist = INT_MAX;
// BfType* bestNegFromType = NULL;
//
// int bestToDist = INT_MAX;
// BfType* bestToType = NULL;
// int bestNegToDist = INT_MAX;
// BfType* bestNegToType = NULL;
//
// for (int pass = 0; pass < 2; pass++)
// {
// BfBaseClassWalker baseClassWalker(fromType, toType, this);
// while (true)
// {
// auto entry = baseClassWalker.Next();
// auto checkInstance = entry.mTypeInstance;
// if (checkInstance == NULL)
// break;
//
// for (auto operatorDef : checkInstance->mTypeDef->mOperators)
// {
// if (operatorDef->mOperatorDeclaration->mIsConvOperator)
// {
// if ((operatorDef->mOperatorDeclaration->mExplicitToken != NULL) &&
// (operatorDef->mOperatorDeclaration->mExplicitToken->GetToken() == BfToken_Explicit))
// continue;
//
// // Get in native module so our module doesn't get a reference to it - we may not end up calling it at all!
// auto methodInst = checkInstance->mModule->GetRawMethodInstanceAtIdx(checkInstance, operatorDef->mIdx);
//
// if (methodInst->GetParamCount() != 1)
// {
// //AssertErrorState();
// continue;
// }
//
// auto checkFromType = methodInst->GetParamType(0);
// if (checkFromType->IsSelf())
// checkFromType = entry.mSrcType;
//
// // Selection pass
// if (pass == 0)
// {
// int fromDist = GetTypeDistance(checkFromType, fromType);
// int toDist = GetTypeDistance(toType, methodInst->mReturnType);
//
// if ((fromDist == INT_MAX) || (toDist == INT_MAX))
// continue;
//
// if ((fromDist >= 0) && (toDist >= 0))
// {
// if ((fromDist >= 0) && (fromDist < bestFromDist))
// {
// bestFromDist = fromDist;
// bestFromType = checkFromType;
// }
//
// if ((fromDist >= 0) && (toDist < bestToDist))
// {
// bestToDist = toDist;
// bestToType = methodInst->mReturnType;
// }
// }
// }
// else // Execution Pass
// {
// auto returnType = methodInst->mReturnType;
// if (returnType->IsSelf())
// returnType = entry.mSrcType;
//
// if ((checkFromType == bestFromType) && (methodInst->mReturnType == bestToType))
// {
// return true;
// }
// }
// }
// }
// }
//
// if (bestFromType == NULL)
// bestFromType = bestNegFromType;
// if (bestToType == NULL)
// bestToType = bestNegToType;
//
// if ((bestFromType == NULL) || (bestToType == NULL))
// break;
// }
// }
// Check user-defined operators
if ((castFlags & BfCastFlags_NoConversionOperator) == 0)
{
auto fromType = typedVal.mType;
auto fromTypeInstance = fromType->ToTypeInstance();
auto toTypeInstance = toType->ToTypeInstance();
auto liftedFromType = ((fromTypeInstance != NULL) && fromTypeInstance->IsNullable()) ? fromTypeInstance->GetUnderlyingType() : NULL;
auto liftedToType = ((toTypeInstance != NULL) && toTypeInstance->IsNullable()) ? toTypeInstance->GetUnderlyingType() : NULL;
int bestFromDist = INT_MAX;
BfType* bestFromType = NULL;
int bestNegFromDist = INT_MAX;
BfType* bestNegFromType = NULL;
int bestToDist = INT_MAX;
BfType* bestToType = NULL;
int bestNegToDist = INT_MAX;
BfType* bestNegToType = NULL;
bool isAmbiguousCast = false;
BfIRValue conversionResult;
BfMethodInstance* opMethodInstance = NULL;
BfType* opMethodSrcType = NULL;
// Normal, lifted, execute
for (int pass = 0; pass < 3; pass++)
{
auto checkToType = toType;
auto checkFromType = fromType;
if (pass == 1)
{
if ((bestFromType != NULL) && (bestToType != NULL))
continue;
if (liftedFromType != NULL)
checkFromType = liftedFromType;
if (liftedToType != NULL)
checkToType = liftedToType;
}
else if (pass == 2)
{
if ((bestFromType == NULL) || (bestToType == NULL))
break;
}
BfBaseClassWalker baseClassWalker(fromType, toType, this);
while (true)
{
auto entry = baseClassWalker.Next();
auto checkInstance = entry.mTypeInstance;
if (checkInstance == NULL)
break;
for (auto operatorDef : checkInstance->mTypeDef->mOperators)
{
if (operatorDef->mOperatorDeclaration->mIsConvOperator)
{
if ((operatorDef->mOperatorDeclaration->mExplicitToken != NULL) &&
(operatorDef->mOperatorDeclaration->mExplicitToken->GetToken() == BfToken_Explicit))
continue;
auto methodInst = GetRawMethodInstanceAtIdx(checkInstance, operatorDef->mIdx);
if (methodInst->GetParamCount() != 1)
{
BF_ASSERT(mCompiler->mPassInstance->HasFailed());
continue;
}
auto methodFromType = methodInst->GetParamType(0);
auto methodToType = methodInst->mReturnType;
if (methodFromType->IsSelf())
methodFromType = entry.mSrcType;
if (methodToType->IsSelf())
methodToType = entry.mSrcType;
// Selection pass
if (pass < 2)
{
auto methodCheckFromType = methodFromType;
auto methodCheckToType = methodToType;
if (pass == 1)
{
// Only check inner type on lifted types when we aren't checking conversions within lifted class
// This avoid some infinite conversions
if ((methodCheckFromType->IsNullable()) && (!checkInstance->IsNullable()))
methodCheckFromType = methodCheckFromType->GetUnderlyingType();
if ((methodCheckToType->IsNullable()) && (!checkInstance->IsNullable()))
methodCheckToType = methodCheckToType->GetUnderlyingType();
}
int fromDist = GetTypeDistance(methodCheckFromType, checkFromType);
if (fromDist < 0)
{
// Allow us to cast a constant int to a smaller type if it satisfies the cast operator
if ((typedVal.mValue.IsConst()) && (methodCheckFromType != toType) && (CanImplicitlyCast(typedVal, methodCheckFromType)))
{
fromDist = 0;
}
}
int toDist = GetTypeDistance(methodCheckToType, checkToType);
if ((fromDist == INT_MAX) || (toDist == INT_MAX))
continue;
if ((fromDist >= 0) && (toDist >= 0))
{
if ((fromDist >= 0) && (fromDist < bestFromDist))
{
bestFromDist = fromDist;
bestFromType = methodFromType;
}
if ((toDist >= 0) && (toDist < bestToDist))
{
bestToDist = toDist;
bestToType = methodToType;
}
}
}
else if (pass == 2) // Execution Pass
{
if ((methodFromType == bestFromType) && (methodToType == bestToType))
{
return CanImplicitlyCast(BfTypedValue(BfIRValue::sValueless, bestToType), toType, castFlags);
}
}
}
}
if (isAmbiguousCast)
break;
}
if (bestFromType == NULL)
bestFromType = bestNegFromType;
if (bestToType == NULL)
bestToType = bestNegToType;
}
}
// Prim -> TypedPrimitive
if ((typedVal.mType->IsPrimitiveType()) && (toType->IsTypedPrimitive()))
{
bool allowCast = false;
if (toType == mCurTypeInstance)
allowCast = true;
if ((!allowCast) && (typedVal.mType->IsIntegral()) /*&& (!toType->IsEnum())*/)
{
// Allow implicit cast of zero
auto constant = mBfIRBuilder->GetConstant(typedVal.mValue);
if ((constant != NULL) && (mBfIRBuilder->IsInt(constant->mTypeCode)))
{
allowCast = constant->mInt64 == 0;
}
}
if (allowCast)
{
return CanImplicitlyCast(typedVal, toType->GetUnderlyingType(), castFlags);
}
}
return false;
}
// This flow should mirror CastToValue
bool BfModule::CanImplicitlyCast(BfTypedValue typedVal, BfType* toType, BfCastFlags castFlags)
{
BP_ZONE("BfModule::CanImplicitlyCast");
//return DoCanImplicitlyCast(typedVal, toType, castFlags);
bool canCastToValue;
{
SetAndRestoreValue<bool> prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, true);
canCastToValue = CastToValue(NULL, typedVal, toType, (BfCastFlags)(castFlags | BfCastFlags_SilentFail));
return canCastToValue;
}
return canCastToValue;
bool sideCanCast = DoCanImplicitlyCast(typedVal, toType, castFlags);
if (canCastToValue != sideCanCast)
{
NOP;
}
//BF_ASSERT(canCastToValue == sideCanCast);
return sideCanCast;
SetAndRestoreValue<bool> prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, true);
return CastToValue(NULL, typedVal, toType, (BfCastFlags)(castFlags | BfCastFlags_SilentFail));
}
bool BfModule::AreSplatsCompatible(BfType* fromType, BfType* toType, bool* outNeedsMemberCasting)
{
if ((fromType->IsTypeInstance()) && (!fromType->IsSplattable()))

View file

@ -94,13 +94,13 @@ namespace Tests
IHashable ihB = valB;
IHashable ihC = valC;
Test.Assert(ihA.GetHashCode() == (int)valA);
Test.Assert(ihB.GetHashCode() == (int)valB);
Test.Assert(ihC.GetHashCode() == (int)valC);
Test.Assert(ihA.GetHashCode() == (int)(void*)valA);
Test.Assert(ihB.GetHashCode() == (int)(void*)valB);
Test.Assert(ihC.GetHashCode() == (int)(void*)valC);
Test.Assert(GetHash(ihA) == (int)valA);
Test.Assert(GetHash(ihB) == (int)valB);
Test.Assert(GetHash(ihC) == (int)valC);
Test.Assert(GetHash(ihA) == (int)(void*)valA);
Test.Assert(GetHash(ihB) == (int)(void*)valB);
Test.Assert(GetHash(ihC) == (int)(void*)valC);
}
}
}