diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index c18c1435..6f283e9f 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -11835,8 +11835,8 @@ void BfExprEvaluator::Visit(BfDynamicCastExpression* dynCastExpr) { auto targetValue = mModule->CreateValueFromExpression(dynCastExpr->mTarget); auto targetType = mModule->ResolveTypeRefAllowUnboundGenerics(dynCastExpr->mTypeRef, BfPopulateType_Data, false); - - auto autoComplete = GetAutoComplete(); + + auto autoComplete = GetAutoComplete(); if (autoComplete != NULL) { autoComplete->CheckTypeRef(dynCastExpr->mTypeRef, false, true); @@ -11873,25 +11873,32 @@ void BfExprEvaluator::Visit(BfDynamicCastExpression* dynCastExpr) } mModule->AddDependency(targetType, mModule->mCurTypeInstance, BfDependencyMap::DependencyFlag_ExprTypeReference); - + if (targetType->IsGenericParam()) { wasGenericParamType = true; - //wasGenericParamType = false; // "was", not "is" - auto genericParamType = (BfGenericParamType*) targetType; - auto genericParam = mModule->GetGenericParamInstance(genericParamType); - auto typeConstraint = genericParam->mTypeConstraint; - if ((typeConstraint == NULL) && (genericParam->mGenericParamFlags & (BfGenericParamFlag_Class | BfGenericParamFlag_Interface))) - typeConstraint = mModule->mContext->mBfObjectType; - - if ((typeConstraint == NULL) || (!typeConstraint->IsObject())) + BfGenericParamInstance* origGenericParam = NULL; + int pass = 0; + while ((targetType != NULL) && (targetType->IsGenericParam())) { - 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", - genericParam->GetGenericParamDef()->mName.c_str()), dynCastExpr->mTypeRef); - return; + auto genericParamType = (BfGenericParamType*)targetType; + auto genericParam = mModule->GetGenericParamInstance(genericParamType); + 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()) @@ -11930,7 +11937,7 @@ void BfExprEvaluator::Visit(BfDynamicCastExpression* dynCastExpr) auto _CheckResult = [&]() { if ((mResult) && (origTargetType->IsGenericParam())) - mResult = mModule->GetDefaultTypedValue(origTargetType); + mResult = mModule->GetDefaultTypedValue(origTargetType, false, BfDefaultValueKind_Undef); }; if ((targetValue.mType->IsNullable()) && (targetType->IsInterface())) @@ -12053,7 +12060,7 @@ void BfExprEvaluator::Visit(BfDynamicCastExpression* dynCastExpr) mModule->Fail("Invalid dynamic cast type", dynCastExpr->mTypeRef); return; } - + BfTypeInstance* srcTypeInstance = targetValue.mType->ToTypeInstance(); BfTypeInstance* targetTypeInstance = targetType->ToTypeInstance(); diff --git a/IDEHelper/Compiler/BfStmtEvaluator.cpp b/IDEHelper/Compiler/BfStmtEvaluator.cpp index c824101e..ea686fe8 100644 --- a/IDEHelper/Compiler/BfStmtEvaluator.cpp +++ b/IDEHelper/Compiler/BfStmtEvaluator.cpp @@ -1197,15 +1197,24 @@ void BfModule::TryInitVar(BfAstNode* checkNode, BfLocalVariable* localVar, BfTyp bool isDynamicCast = false; if (varType->IsGenericParam()) - { - auto genericParamType = (BfGenericParamType*) varType; - auto genericParam = GetGenericParamInstance(genericParamType); - auto typeConstraint = genericParam->mTypeConstraint; - if ((typeConstraint == NULL) && (genericParam->mGenericParamFlags & BfGenericParamFlag_Class)) - typeConstraint = mContext->mBfObjectType; - if (typeConstraint != NULL) - varType = typeConstraint; - initValue = GetDefaultTypedValue(varType); + { + int pass = 0; + while (varType->IsGenericParam()) + { + auto genericParamType = (BfGenericParamType*)varType; + + auto genericParam = GetGenericParamInstance(genericParamType); + auto typeConstraint = genericParam->mTypeConstraint; + 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(); diff --git a/IDEHelper/Tests/src/Generics2.bf b/IDEHelper/Tests/src/Generics2.bf index 7f42cba0..22c7694f 100644 --- a/IDEHelper/Tests/src/Generics2.bf +++ b/IDEHelper/Tests/src/Generics2.bf @@ -165,6 +165,19 @@ namespace Tests public static int GenClassMethodB(GenClass a) { return a.test++; } public static int GenClassMethodC(A a) where A : GenClass { return a.test += 1; } + public static TDerived AssertSubtype(TBase instance) + where TDerived : TBase + where TBase : class + { + if (instance == null) + Runtime.FatalError(); + if (TDerived derived = instance as TDerived) + { + return derived; + } + Runtime.FatalError(); + } + [Test] public static void TestBasics() {