1
0
Fork 0
mirror of https://github.com/beefytech/Beef.git synced 2025-06-11 04:52:21 +02:00

Improved type constraint checks constrained by other generic params

This commit is contained in:
Brian Fiete 2022-06-11 07:56:43 -07:00
parent 10682e67af
commit 06ceaf617b
3 changed files with 55 additions and 26 deletions

View file

@ -11835,8 +11835,8 @@ void BfExprEvaluator::Visit(BfDynamicCastExpression* dynCastExpr)
{ {
auto targetValue = mModule->CreateValueFromExpression(dynCastExpr->mTarget); auto targetValue = mModule->CreateValueFromExpression(dynCastExpr->mTarget);
auto targetType = mModule->ResolveTypeRefAllowUnboundGenerics(dynCastExpr->mTypeRef, BfPopulateType_Data, false); auto targetType = mModule->ResolveTypeRefAllowUnboundGenerics(dynCastExpr->mTypeRef, BfPopulateType_Data, false);
auto autoComplete = GetAutoComplete(); auto autoComplete = GetAutoComplete();
if (autoComplete != NULL) if (autoComplete != NULL)
{ {
autoComplete->CheckTypeRef(dynCastExpr->mTypeRef, false, true); autoComplete->CheckTypeRef(dynCastExpr->mTypeRef, false, true);
@ -11873,25 +11873,32 @@ void BfExprEvaluator::Visit(BfDynamicCastExpression* dynCastExpr)
} }
mModule->AddDependency(targetType, mModule->mCurTypeInstance, BfDependencyMap::DependencyFlag_ExprTypeReference); mModule->AddDependency(targetType, mModule->mCurTypeInstance, BfDependencyMap::DependencyFlag_ExprTypeReference);
if (targetType->IsGenericParam()) if (targetType->IsGenericParam())
{ {
wasGenericParamType = true; wasGenericParamType = true;
//wasGenericParamType = false; // "was", not "is" BfGenericParamInstance* origGenericParam = NULL;
auto genericParamType = (BfGenericParamType*) targetType; int pass = 0;
auto genericParam = mModule->GetGenericParamInstance(genericParamType); while ((targetType != NULL) && (targetType->IsGenericParam()))
auto typeConstraint = genericParam->mTypeConstraint;
if ((typeConstraint == NULL) && (genericParam->mGenericParamFlags & (BfGenericParamFlag_Class | BfGenericParamFlag_Interface)))
typeConstraint = mModule->mContext->mBfObjectType;
if ((typeConstraint == NULL) || (!typeConstraint->IsObject()))
{ {
mModule->Fail(StrFormat("The type parameter '%s' cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' or 'interface' constraint", auto genericParamType = (BfGenericParamType*)targetType;
genericParam->GetGenericParamDef()->mName.c_str()), dynCastExpr->mTypeRef); auto genericParam = mModule->GetGenericParamInstance(genericParamType);
return; if (pass == 0)
origGenericParam = genericParam;
auto typeConstraint = genericParam->mTypeConstraint;
if ((typeConstraint == NULL) && (genericParam->mGenericParamFlags & (BfGenericParamFlag_Class | BfGenericParamFlag_Interface)))
typeConstraint = mModule->mContext->mBfObjectType;
targetType = typeConstraint;
if (++pass >= 100) // Sanity - but we should have caught circular error before
break;
} }
targetType = typeConstraint; if ((targetType == NULL) || (!targetType->IsObjectOrInterface()))
{
mModule->Fail(StrFormat("The type parameter '%s' cannot be used with the 'as' operator because it does not have a class type constraint nor a 'class' or 'interface' constraint",
origGenericParam->GetGenericParamDef()->mName.c_str()), dynCastExpr->mTypeRef);
return;
}
} }
if (targetType->IsVar()) if (targetType->IsVar())
@ -11930,7 +11937,7 @@ void BfExprEvaluator::Visit(BfDynamicCastExpression* dynCastExpr)
auto _CheckResult = [&]() auto _CheckResult = [&]()
{ {
if ((mResult) && (origTargetType->IsGenericParam())) if ((mResult) && (origTargetType->IsGenericParam()))
mResult = mModule->GetDefaultTypedValue(origTargetType); mResult = mModule->GetDefaultTypedValue(origTargetType, false, BfDefaultValueKind_Undef);
}; };
if ((targetValue.mType->IsNullable()) && (targetType->IsInterface())) if ((targetValue.mType->IsNullable()) && (targetType->IsInterface()))
@ -12053,7 +12060,7 @@ void BfExprEvaluator::Visit(BfDynamicCastExpression* dynCastExpr)
mModule->Fail("Invalid dynamic cast type", dynCastExpr->mTypeRef); mModule->Fail("Invalid dynamic cast type", dynCastExpr->mTypeRef);
return; return;
} }
BfTypeInstance* srcTypeInstance = targetValue.mType->ToTypeInstance(); BfTypeInstance* srcTypeInstance = targetValue.mType->ToTypeInstance();
BfTypeInstance* targetTypeInstance = targetType->ToTypeInstance(); BfTypeInstance* targetTypeInstance = targetType->ToTypeInstance();

View file

@ -1197,15 +1197,24 @@ void BfModule::TryInitVar(BfAstNode* checkNode, BfLocalVariable* localVar, BfTyp
bool isDynamicCast = false; bool isDynamicCast = false;
if (varType->IsGenericParam()) if (varType->IsGenericParam())
{ {
auto genericParamType = (BfGenericParamType*) varType; int pass = 0;
auto genericParam = GetGenericParamInstance(genericParamType); while (varType->IsGenericParam())
auto typeConstraint = genericParam->mTypeConstraint; {
if ((typeConstraint == NULL) && (genericParam->mGenericParamFlags & BfGenericParamFlag_Class)) auto genericParamType = (BfGenericParamType*)varType;
typeConstraint = mContext->mBfObjectType;
if (typeConstraint != NULL) auto genericParam = GetGenericParamInstance(genericParamType);
varType = typeConstraint; auto typeConstraint = genericParam->mTypeConstraint;
initValue = GetDefaultTypedValue(varType); if ((typeConstraint == NULL) && (genericParam->mGenericParamFlags & (BfGenericParamFlag_Class | BfGenericParamFlag_Interface)))
typeConstraint = mContext->mBfObjectType;
if (typeConstraint != NULL)
varType = typeConstraint;
else
break;
if (++pass >= 100) // Sanity - but we should have caught circular error before
break;
}
initValue = GetDefaultTypedValue(varType, false, BfDefaultValueKind_Undef);
} }
BfTypeInstance* srcTypeInstance = initValue.mType->ToTypeInstance(); BfTypeInstance* srcTypeInstance = initValue.mType->ToTypeInstance();

View file

@ -165,6 +165,19 @@ namespace Tests
public static int GenClassMethodB(GenClass<int> a) { return a.test++; } public static int GenClassMethodB(GenClass<int> a) { return a.test++; }
public static int GenClassMethodC<A>(A a) where A : GenClass<int> { return a.test += 1; } public static int GenClassMethodC<A>(A a) where A : GenClass<int> { return a.test += 1; }
public static TDerived AssertSubtype<TBase, TDerived>(TBase instance)
where TDerived : TBase
where TBase : class
{
if (instance == null)
Runtime.FatalError();
if (TDerived derived = instance as TDerived)
{
return derived;
}
Runtime.FatalError();
}
[Test] [Test]
public static void TestBasics() public static void TestBasics()
{ {