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:
parent
098ad1ce55
commit
503261e916
3 changed files with 10 additions and 936 deletions
|
@ -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()))
|
||||
{
|
||||
|
|
|
@ -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()))
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue