1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-09 03:52:19 +02:00

Fixed genericParam->genericParam cast check, IgnoreError constraint fail

This commit is contained in:
Brian Fiete 2021-01-31 08:06:47 -08:00
parent 970ac9add2
commit 1519a60104
4 changed files with 156 additions and 113 deletions

View file

@ -8685,8 +8685,19 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp
BfType* overrideReturnType = NULL; BfType* overrideReturnType = NULL;
BfModuleMethodInstance moduleMethodInstance = GetSelectedMethod(targetSrc, curTypeInst, methodDef, methodMatcher, &overrideReturnType); BfModuleMethodInstance moduleMethodInstance = GetSelectedMethod(targetSrc, curTypeInst, methodDef, methodMatcher, &overrideReturnType);
if ((moduleMethodInstance.mMethodInstance != NULL) && (!mModule->CheckUseMethodInstance(moduleMethodInstance.mMethodInstance, targetSrc)))
if ((mModule->mAttributeState != NULL) && ((mModule->mAttributeState->mFlags & (BfAttributeState::Flag_StopOnError | BfAttributeState::Flag_HadError)) ==
(BfAttributeState::Flag_StopOnError | BfAttributeState::Flag_HadError)))
{
FinishDeferredEvals(argValues);
return BfTypedValue(); return BfTypedValue();
}
if ((moduleMethodInstance.mMethodInstance != NULL) && (!mModule->CheckUseMethodInstance(moduleMethodInstance.mMethodInstance, targetSrc)))
{
FinishDeferredEvals(argValues);
return BfTypedValue();
}
if ((mModule->mCurMethodInstance != NULL) && (mModule->mCurMethodInstance->mIsUnspecialized)) if ((mModule->mCurMethodInstance != NULL) && (mModule->mCurMethodInstance->mIsUnspecialized))
{ {

View file

@ -7343,10 +7343,9 @@ void BfModule::ResolveGenericParamConstraints(BfGenericParamInstance* genericPar
if (constraintType->IsGenericParam()) if (constraintType->IsGenericParam())
{ {
continue; checkEquality = true;
} }
else if ((!constraintType->IsTypeInstance()) && (!constraintType->IsSizedArray()))
if ((!constraintType->IsTypeInstance()) && (!constraintType->IsSizedArray()))
{ {
if (isUnspecialized) if (isUnspecialized)
{ {
@ -7435,7 +7434,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
return TypeToString(type); return TypeToString(type);
}; };
bool ignoreErrors = mIgnoreErrors || (errorOut == NULL) || bool ignoreErrors = (errorOut == NULL) ||
((genericParamSource.mMethodInstance == NULL) && (genericParamSource.mTypeInstance == NULL)); ((genericParamSource.mMethodInstance == NULL) && (genericParamSource.mTypeInstance == NULL));
BfType* origCheckArgType = checkArgType; BfType* origCheckArgType = checkArgType;
@ -7473,7 +7472,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Struct) && if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Struct) &&
((checkGenericParamFlags & (BfGenericParamFlag_Struct | BfGenericParamFlag_Enum | BfGenericParamFlag_Var)) == 0) && (!checkArgType->IsValueType())) ((checkGenericParamFlags & (BfGenericParamFlag_Struct | BfGenericParamFlag_Enum | BfGenericParamFlag_Var)) == 0) && (!checkArgType->IsValueType()))
{ {
if (!ignoreErrors) if ((!ignoreErrors) && (PreFail()))
*errorOut = Fail(StrFormat("The type '%s' must be a value type in order to use it as parameter '%s' for '%s'", *errorOut = Fail(StrFormat("The type '%s' must be a value type in order to use it as parameter '%s' for '%s'",
TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
return false; return false;
@ -7482,7 +7481,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_StructPtr) && if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_StructPtr) &&
((checkGenericParamFlags & (BfGenericParamFlag_StructPtr | BfGenericParamFlag_Var)) == 0) && (!checkArgType->IsPointer())) ((checkGenericParamFlags & (BfGenericParamFlag_StructPtr | BfGenericParamFlag_Var)) == 0) && (!checkArgType->IsPointer()))
{ {
if (!ignoreErrors) if ((!ignoreErrors) && (PreFail()))
*errorOut = Fail(StrFormat("The type '%s' must be a pointer type in order to use it as parameter '%s' for '%s'", *errorOut = Fail(StrFormat("The type '%s' must be a pointer type in order to use it as parameter '%s' for '%s'",
TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
return false; return false;
@ -7491,7 +7490,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Class) && if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Class) &&
((checkGenericParamFlags & (BfGenericParamFlag_Class | BfGenericParamFlag_Var)) == 0) && (!argMayBeReferenceType)) ((checkGenericParamFlags & (BfGenericParamFlag_Class | BfGenericParamFlag_Var)) == 0) && (!argMayBeReferenceType))
{ {
if (!ignoreErrors) if ((!ignoreErrors) && (PreFail()))
*errorOut = Fail(StrFormat("The type '%s' must be a reference type in order to use it as parameter '%s' for '%s'", *errorOut = Fail(StrFormat("The type '%s' must be a reference type in order to use it as parameter '%s' for '%s'",
TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
return false; return false;
@ -7504,7 +7503,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
isEnum = true; isEnum = true;
if (((checkGenericParamFlags & (BfGenericParamFlag_Enum | BfGenericParamFlag_Var)) == 0) && (!isEnum)) if (((checkGenericParamFlags & (BfGenericParamFlag_Enum | BfGenericParamFlag_Var)) == 0) && (!isEnum))
{ {
if (!ignoreErrors) if ((!ignoreErrors) && (PreFail()))
*errorOut = Fail(StrFormat("The type '%s' must be an enum type in order to use it as parameter '%s' for '%s'", *errorOut = Fail(StrFormat("The type '%s' must be an enum type in order to use it as parameter '%s' for '%s'",
TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
return false; return false;
@ -7514,7 +7513,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Concrete) && if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Concrete) &&
((checkGenericParamFlags & (BfGenericParamFlag_Interface | BfGenericParamFlag_Var)) == 0) && (checkArgType->IsInterface())) ((checkGenericParamFlags & (BfGenericParamFlag_Interface | BfGenericParamFlag_Var)) == 0) && (checkArgType->IsInterface()))
{ {
if (!ignoreErrors) if ((!ignoreErrors) && (PreFail()))
*errorOut = Fail(StrFormat("The type '%s' must be an concrete type in order to use it as parameter '%s' for '%s'", *errorOut = Fail(StrFormat("The type '%s' must be an concrete type in order to use it as parameter '%s' for '%s'",
TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
return false; return false;
@ -7523,7 +7522,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Interface) && if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Interface) &&
((checkGenericParamFlags & (BfGenericParamFlag_Interface | BfGenericParamFlag_Var)) == 0) && (!checkArgType->IsInterface())) ((checkGenericParamFlags & (BfGenericParamFlag_Interface | BfGenericParamFlag_Var)) == 0) && (!checkArgType->IsInterface()))
{ {
if (!ignoreErrors) if ((!ignoreErrors) && (PreFail()))
*errorOut = Fail(StrFormat("The type '%s' must be an interface type in order to use it as parameter '%s' for '%s'", *errorOut = Fail(StrFormat("The type '%s' must be an interface type in order to use it as parameter '%s' for '%s'",
TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
return false; return false;
@ -7533,7 +7532,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
{ {
if (((checkGenericParamFlags & BfGenericParamFlag_Const) == 0) && (!checkArgType->IsConstExprValue())) if (((checkGenericParamFlags & BfGenericParamFlag_Const) == 0) && (!checkArgType->IsConstExprValue()))
{ {
if (!ignoreErrors) if ((!ignoreErrors) && (PreFail()))
*errorOut = Fail(StrFormat("The type '%s' must be a const value in order to use it as parameter '%s' for '%s'", *errorOut = Fail(StrFormat("The type '%s' must be a const value in order to use it as parameter '%s' for '%s'",
TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
return false; return false;
@ -7543,7 +7542,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
{ {
if (checkArgType->IsConstExprValue()) if (checkArgType->IsConstExprValue())
{ {
if (!ignoreErrors) if ((!ignoreErrors) && (PreFail()))
*errorOut = Fail(StrFormat("The value '%s' cannot be used for generic type parameter '%s' for '%s'", *errorOut = Fail(StrFormat("The value '%s' cannot be used for generic type parameter '%s' for '%s'",
TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
return false; return false;
@ -7562,7 +7561,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
if (!canDelete) if (!canDelete)
{ {
if (!ignoreErrors) if ((!ignoreErrors) && (PreFail()))
*errorOut = Fail(StrFormat("The type '%s' must be a deletable type in order to use it as parameter '%s' for '%s'", *errorOut = Fail(StrFormat("The type '%s' must be a deletable type in order to use it as parameter '%s' for '%s'",
TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
return false; return false;
@ -7607,7 +7606,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
if (!canAlloc) if (!canAlloc)
{ {
if (!ignoreErrors) if ((!ignoreErrors) && (PreFail()))
*errorOut = Fail(StrFormat("The type '%s' must have an accessible default constructor in order to use it as parameter '%s' for '%s'", *errorOut = Fail(StrFormat("The type '%s' must have an accessible default constructor in order to use it as parameter '%s' for '%s'",
TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
return false; return false;
@ -7649,7 +7648,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
{ {
if (!mCompiler->mSystem->DoesLiteralFit(primType->mTypeDef->mTypeCode, constExprValueType->mValue.mInt64)) if (!mCompiler->mSystem->DoesLiteralFit(primType->mTypeDef->mTypeCode, constExprValueType->mValue.mInt64))
{ {
if (!ignoreErrors) if ((!ignoreErrors) && (PreFail()))
*errorOut = Fail(StrFormat("Const generic argument '%s', declared with const '%lld', does not fit into const constraint '%s' for '%s'", genericParamInst->GetName().c_str(), *errorOut = Fail(StrFormat("Const generic argument '%s', declared with const '%lld', does not fit into const constraint '%s' for '%s'", genericParamInst->GetName().c_str(),
constExprValueType->mValue.mInt64, _TypeToString(genericParamInst->mTypeConstraint).c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); constExprValueType->mValue.mInt64, _TypeToString(genericParamInst->mTypeConstraint).c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
return false; return false;
@ -7657,7 +7656,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
} }
else else
{ {
if (!ignoreErrors) if ((!ignoreErrors) && (PreFail()))
*errorOut = Fail(StrFormat("Const generic argument '%s', declared with integer const '%lld', is not compatible with const constraint '%s' for '%s'", genericParamInst->GetName().c_str(), *errorOut = Fail(StrFormat("Const generic argument '%s', declared with integer const '%lld', is not compatible with const constraint '%s' for '%s'", genericParamInst->GetName().c_str(),
constExprValueType->mValue.mInt64, _TypeToString(genericParamInst->mTypeConstraint).c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); constExprValueType->mValue.mInt64, _TypeToString(genericParamInst->mTypeConstraint).c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
return false; return false;
@ -7669,7 +7668,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
{ {
char valStr[64]; char valStr[64];
ExactMinimalDoubleToStr(constExprValueType->mValue.mDouble, valStr); ExactMinimalDoubleToStr(constExprValueType->mValue.mDouble, valStr);
if (!ignoreErrors) if ((!ignoreErrors) && (PreFail()))
*errorOut = Fail(StrFormat("Const generic argument '%s', declared with floating point const '%s', is not compatible with const constraint '%s' for '%s'", genericParamInst->GetName().c_str(), *errorOut = Fail(StrFormat("Const generic argument '%s', declared with floating point const '%s', is not compatible with const constraint '%s' for '%s'", genericParamInst->GetName().c_str(),
valStr, _TypeToString(genericParamInst->mTypeConstraint).c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); valStr, _TypeToString(genericParamInst->mTypeConstraint).c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef);
return false; return false;
@ -7740,7 +7739,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
if (!constraintMatched) if (!constraintMatched)
{ {
if (!ignoreErrors) if ((!ignoreErrors) && (PreFail()))
*errorOut = Fail(StrFormat("Generic argument '%s', declared to be '%s' for '%s', must derive from '%s'", genericParamInst->GetName().c_str(), *errorOut = Fail(StrFormat("Generic argument '%s', declared to be '%s' for '%s', must derive from '%s'", genericParamInst->GetName().c_str(),
TypeToString(origCheckArgType).c_str(), GenericParamSourceToString(genericParamSource).c_str(), TypeToString(convCheckConstraint).c_str(), TypeToString(origCheckArgType).c_str(), GenericParamSourceToString(genericParamSource).c_str(), TypeToString(convCheckConstraint).c_str(),
_TypeToString(genericParamInst->mTypeConstraint).c_str()), checkArgTypeRef); _TypeToString(genericParamInst->mTypeConstraint).c_str()), checkArgTypeRef);
@ -7785,7 +7784,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
if (!implementsInterface) if (!implementsInterface)
{ {
if (!ignoreErrors) if ((!ignoreErrors) && (PreFail()))
*errorOut = Fail(StrFormat("Generic argument '%s', declared to be '%s' for '%s', must implement '%s'", genericParamInst->GetName().c_str(), *errorOut = Fail(StrFormat("Generic argument '%s', declared to be '%s' for '%s', must implement '%s'", genericParamInst->GetName().c_str(),
TypeToString(origCheckArgType).c_str(), GenericParamSourceToString(genericParamSource).c_str(), TypeToString(checkConstraint).c_str()), checkArgTypeRef); TypeToString(origCheckArgType).c_str(), GenericParamSourceToString(genericParamSource).c_str(), TypeToString(checkConstraint).c_str()), checkArgTypeRef);
return false; return false;
@ -7832,7 +7831,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
if ((!exprEvaluator.mResult) || if ((!exprEvaluator.mResult) ||
(!CanCast(exprEvaluator.mResult, origCheckArgType, BfCastFlags_NoConversionOperator))) (!CanCast(exprEvaluator.mResult, origCheckArgType, BfCastFlags_NoConversionOperator)))
{ {
if (!ignoreErrors) if ((!ignoreErrors) && (PreFail()))
{ {
if (genericParamInst->mExternType != NULL) if (genericParamInst->mExternType != NULL)
*errorOut = Fail(StrFormat("Binary operation for '%s' must result in '%s' from binary operation '%s %s %s'", *errorOut = Fail(StrFormat("Binary operation for '%s' must result in '%s' from binary operation '%s %s %s'",
@ -7887,7 +7886,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS
if (!failedOpName.IsEmpty()) if (!failedOpName.IsEmpty())
{ {
if (!ignoreErrors) if ((!ignoreErrors) && (PreFail()))
*errorOut = Fail(StrFormat("Generic argument '%s', declared to be '%s' for '%s', must result from %s%s'", genericParamInst->GetName().c_str(), *errorOut = Fail(StrFormat("Generic argument '%s', declared to be '%s' for '%s', must result from %s%s'", genericParamInst->GetName().c_str(),
TypeToString(origCheckArgType).c_str(), GenericParamSourceToString(genericParamSource).c_str(), TypeToString(origCheckArgType).c_str(), GenericParamSourceToString(genericParamSource).c_str(),
failedOpName.c_str(), TypeToString(rightType).c_str() failedOpName.c_str(), TypeToString(rightType).c_str()

View file

@ -10821,104 +10821,113 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp
} }
// Generic param -> * // Generic param -> *
if ((typedVal.mType->IsGenericParam()) && (!toType->IsGenericParam())) if (typedVal.mType->IsGenericParam())
{ {
if ((typedVal.mKind != Beefy::BfTypedValueKind_GenericConstValue) && (toType == mContext->mBfObjectType)) if (toType->IsGenericParam())
{
// Always allow casting from generic to object
return typedVal.mValue;
}
auto _CheckGenericParamInstance = [&](BfGenericParamInstance* genericParamInst)
{
if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Var) != 0)
{
return typedVal.mValue;
}
if (toType->IsInterface())
{
for (auto iface : genericParamInst->mInterfaceConstraints)
if (TypeIsSubTypeOf(iface, toType->ToTypeInstance()))
return mBfIRBuilder->GetFakeVal();
}
if (genericParamInst->mTypeConstraint != NULL)
{
SetAndRestoreValue<bool> prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, true);
auto constraintTypeInst = genericParamInst->mTypeConstraint->ToTypeInstance();
if ((constraintTypeInst != NULL) && (constraintTypeInst->mTypeDef == mCompiler->mEnumTypeDef))
{
// Enum->int
if ((explicitCast) && (toType->IsInteger()))
return typedVal.mValue;
}
BfTypedValue fromTypedValue;
if (typedVal.mKind == BfTypedValueKind_GenericConstValue)
fromTypedValue = GetDefaultTypedValue(genericParamInst->mTypeConstraint, false, BfDefaultValueKind_Undef);
else
fromTypedValue = BfTypedValue(mBfIRBuilder->GetFakeVal(), genericParamInst->mTypeConstraint, genericParamInst->mTypeConstraint->IsValueType());
auto result = CastToValue(srcNode, fromTypedValue, toType, (BfCastFlags)(castFlags | BfCastFlags_SilentFail));
if (result)
{
if ((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
Fail(StrFormat("Unable to cast '%s' to '%s' because delegate constraints allow valueless direct method references", TypeToString(typedVal.mType).c_str(), TypeToString(toType).c_str()), srcNode);
return BfIRValue();
}
return result;
}
}
// Generic constrained with class or pointer type -> void*
if (toType->IsVoidPtr())
{
if (((genericParamInst->mGenericParamFlags & (BfGenericParamFlag_Class | BfGenericParamFlag_StructPtr | BfGenericParamFlag_Interface)) != 0) ||
((genericParamInst->mTypeConstraint != NULL) &&
((genericParamInst->mTypeConstraint->IsPointer()) ||
(genericParamInst->mTypeConstraint->IsInstanceOf(mCompiler->mFunctionTypeDef)) ||
(genericParamInst->mTypeConstraint->IsObjectOrInterface()))))
{
return typedVal.mValue;
}
}
if (toType->IsInteger())
{
if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Enum) != 0)
{
return typedVal.mValue;
}
}
return BfIRValue();
};
BfIRValue retVal;
// 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); auto genericParamInst = GetGenericParamInstance((BfGenericParamType*)typedVal.mType);
retVal = _CheckGenericParamInstance(genericParamInst); if (genericParamInst->mTypeConstraint == toType)
if (retVal) return typedVal.mValue;
return retVal;
} }
else
// Check method generic constraints
if ((mCurMethodInstance != NULL) && (mCurMethodInstance->mIsUnspecialized) && (mCurMethodInstance->mMethodInfoEx != NULL))
{ {
for (int genericParamIdx = (int)mCurMethodInstance->mMethodInfoEx->mMethodGenericArguments.size(); if ((typedVal.mKind != Beefy::BfTypedValueKind_GenericConstValue) && (toType == mContext->mBfObjectType))
genericParamIdx < mCurMethodInstance->mMethodInfoEx->mGenericParams.size(); genericParamIdx++)
{ {
auto genericParamInst = mCurMethodInstance->mMethodInfoEx->mGenericParams[genericParamIdx]; // Always allow casting from generic to object
if (genericParamInst->mExternType == typedVal.mType) return typedVal.mValue;
}
auto _CheckGenericParamInstance = [&](BfGenericParamInstance* genericParamInst)
{
if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Var) != 0)
{ {
retVal = _CheckGenericParamInstance(genericParamInst); return typedVal.mValue;
if (retVal) }
return retVal; if (toType->IsInterface())
{
for (auto iface : genericParamInst->mInterfaceConstraints)
if (TypeIsSubTypeOf(iface, toType->ToTypeInstance()))
return mBfIRBuilder->GetFakeVal();
}
if (genericParamInst->mTypeConstraint != NULL)
{
SetAndRestoreValue<bool> prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, true);
auto constraintTypeInst = genericParamInst->mTypeConstraint->ToTypeInstance();
if ((constraintTypeInst != NULL) && (constraintTypeInst->mTypeDef == mCompiler->mEnumTypeDef))
{
// Enum->int
if ((explicitCast) && (toType->IsInteger()))
return typedVal.mValue;
}
BfTypedValue fromTypedValue;
if (typedVal.mKind == BfTypedValueKind_GenericConstValue)
fromTypedValue = GetDefaultTypedValue(genericParamInst->mTypeConstraint, false, BfDefaultValueKind_Undef);
else
fromTypedValue = BfTypedValue(mBfIRBuilder->GetFakeVal(), genericParamInst->mTypeConstraint, genericParamInst->mTypeConstraint->IsValueType());
auto result = CastToValue(srcNode, fromTypedValue, toType, (BfCastFlags)(castFlags | BfCastFlags_SilentFail));
if (result)
{
if ((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
Fail(StrFormat("Unable to cast '%s' to '%s' because delegate constraints allow valueless direct method references", TypeToString(typedVal.mType).c_str(), TypeToString(toType).c_str()), srcNode);
return BfIRValue();
}
return result;
}
}
// Generic constrained with class or pointer type -> void*
if (toType->IsVoidPtr())
{
if (((genericParamInst->mGenericParamFlags & (BfGenericParamFlag_Class | BfGenericParamFlag_StructPtr | BfGenericParamFlag_Interface)) != 0) ||
((genericParamInst->mTypeConstraint != NULL) &&
((genericParamInst->mTypeConstraint->IsPointer()) ||
(genericParamInst->mTypeConstraint->IsInstanceOf(mCompiler->mFunctionTypeDef)) ||
(genericParamInst->mTypeConstraint->IsObjectOrInterface()))))
{
return typedVal.mValue;
}
}
if (toType->IsInteger())
{
if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Enum) != 0)
{
return typedVal.mValue;
}
}
return BfIRValue();
};
BfIRValue retVal;
// 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);
retVal = _CheckGenericParamInstance(genericParamInst);
if (retVal)
return retVal;
}
// Check method generic constraints
if ((mCurMethodInstance != NULL) && (mCurMethodInstance->mIsUnspecialized) && (mCurMethodInstance->mMethodInfoEx != NULL))
{
for (int genericParamIdx = (int)mCurMethodInstance->mMethodInfoEx->mMethodGenericArguments.size();
genericParamIdx < mCurMethodInstance->mMethodInfoEx->mGenericParams.size(); genericParamIdx++)
{
auto genericParamInst = mCurMethodInstance->mMethodInfoEx->mGenericParams[genericParamIdx];
if (genericParamInst->mExternType == typedVal.mType)
{
retVal = _CheckGenericParamInstance(genericParamInst);
if (retVal)
return retVal;
}
} }
} }
} }

View file

@ -241,6 +241,12 @@ namespace Tests
return 0; return 0;
} }
static void MethodG<T, TBase>() where T : TBase
{
T val = default;
TBase valBase = val;
}
public static TResult Sum<T, TElem, TDlg, TResult>(this T it, TDlg dlg) public static TResult Sum<T, TElem, TDlg, TResult>(this T it, TDlg dlg)
where T: concrete, IEnumerable<TElem> where T: concrete, IEnumerable<TElem>
where TDlg: delegate TResult(TElem) where TDlg: delegate TResult(TElem)
@ -280,6 +286,16 @@ namespace Tests
return .(it.GetEnumerator()); return .(it.GetEnumerator());
} }
class ClassF
{
}
class ClassG : ClassF
{
}
[Test] [Test]
public static void TestBasics() public static void TestBasics()
{ {
@ -339,6 +355,14 @@ namespace Tests
Test.Assert(e.GetNext().Value == 2); Test.Assert(e.GetNext().Value == 2);
Test.Assert(e.GetNext().Value == 4); Test.Assert(e.GetNext().Value == 4);
Test.Assert(e.GetNext().Value == 6); Test.Assert(e.GetNext().Value == 6);
Test.Assert(
[IgnoreErrors(true)]
{
MethodG<ClassF, ClassG>();
true
} == false);
MethodG<ClassG, ClassF>();
} }
} }