diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index 305b57be..25f0ba51 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -8685,8 +8685,19 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp BfType* overrideReturnType = NULL; 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(); + } + + if ((moduleMethodInstance.mMethodInstance != NULL) && (!mModule->CheckUseMethodInstance(moduleMethodInstance.mMethodInstance, targetSrc))) + { + FinishDeferredEvals(argValues); + return BfTypedValue(); + } if ((mModule->mCurMethodInstance != NULL) && (mModule->mCurMethodInstance->mIsUnspecialized)) { diff --git a/IDEHelper/Compiler/BfModule.cpp b/IDEHelper/Compiler/BfModule.cpp index 5ac5bd8d..41687e09 100644 --- a/IDEHelper/Compiler/BfModule.cpp +++ b/IDEHelper/Compiler/BfModule.cpp @@ -7343,10 +7343,9 @@ void BfModule::ResolveGenericParamConstraints(BfGenericParamInstance* genericPar if (constraintType->IsGenericParam()) { - continue; + checkEquality = true; } - - if ((!constraintType->IsTypeInstance()) && (!constraintType->IsSizedArray())) + else if ((!constraintType->IsTypeInstance()) && (!constraintType->IsSizedArray())) { if (isUnspecialized) { @@ -7435,7 +7434,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS return TypeToString(type); }; - bool ignoreErrors = mIgnoreErrors || (errorOut == NULL) || + bool ignoreErrors = (errorOut == NULL) || ((genericParamSource.mMethodInstance == NULL) && (genericParamSource.mTypeInstance == NULL)); BfType* origCheckArgType = checkArgType; @@ -7473,7 +7472,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Struct) && ((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'", TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); return false; @@ -7482,7 +7481,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_StructPtr) && ((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'", TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); return false; @@ -7491,7 +7490,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Class) && ((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'", TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); return false; @@ -7504,7 +7503,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS isEnum = true; 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'", TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); return false; @@ -7514,7 +7513,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Concrete) && ((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'", TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); return false; @@ -7523,7 +7522,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Interface) && ((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'", TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); return false; @@ -7533,7 +7532,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS { 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'", TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); return false; @@ -7543,7 +7542,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS { if (checkArgType->IsConstExprValue()) { - if (!ignoreErrors) + if ((!ignoreErrors) && (PreFail())) *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); return false; @@ -7562,7 +7561,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS 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'", TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); return false; @@ -7607,7 +7606,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS 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'", TypeToString(origCheckArgType).c_str(), genericParamInst->GetName().c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); return false; @@ -7649,7 +7648,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS { 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(), constExprValueType->mValue.mInt64, _TypeToString(genericParamInst->mTypeConstraint).c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); return false; @@ -7657,7 +7656,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS } 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(), constExprValueType->mValue.mInt64, _TypeToString(genericParamInst->mTypeConstraint).c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); return false; @@ -7669,7 +7668,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS { char valStr[64]; 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(), valStr, _TypeToString(genericParamInst->mTypeConstraint).c_str(), GenericParamSourceToString(genericParamSource).c_str()), checkArgTypeRef); return false; @@ -7740,7 +7739,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS 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(), TypeToString(origCheckArgType).c_str(), GenericParamSourceToString(genericParamSource).c_str(), TypeToString(convCheckConstraint).c_str(), _TypeToString(genericParamInst->mTypeConstraint).c_str()), checkArgTypeRef); @@ -7785,7 +7784,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS 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(), TypeToString(origCheckArgType).c_str(), GenericParamSourceToString(genericParamSource).c_str(), TypeToString(checkConstraint).c_str()), checkArgTypeRef); return false; @@ -7832,7 +7831,7 @@ bool BfModule::CheckGenericConstraints(const BfGenericParamSource& genericParamS if ((!exprEvaluator.mResult) || (!CanCast(exprEvaluator.mResult, origCheckArgType, BfCastFlags_NoConversionOperator))) { - if (!ignoreErrors) + if ((!ignoreErrors) && (PreFail())) { if (genericParamInst->mExternType != NULL) *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 (!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(), TypeToString(origCheckArgType).c_str(), GenericParamSourceToString(genericParamSource).c_str(), failedOpName.c_str(), TypeToString(rightType).c_str() diff --git a/IDEHelper/Compiler/BfModuleTypeUtils.cpp b/IDEHelper/Compiler/BfModuleTypeUtils.cpp index a559873b..90e2af06 100644 --- a/IDEHelper/Compiler/BfModuleTypeUtils.cpp +++ b/IDEHelper/Compiler/BfModuleTypeUtils.cpp @@ -10821,104 +10821,113 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp } // Generic param -> * - if ((typedVal.mType->IsGenericParam()) && (!toType->IsGenericParam())) + if (typedVal.mType->IsGenericParam()) { - if ((typedVal.mKind != Beefy::BfTypedValueKind_GenericConstValue) && (toType == mContext->mBfObjectType)) - { - // 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 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 + if (toType->IsGenericParam()) { auto genericParamInst = GetGenericParamInstance((BfGenericParamType*)typedVal.mType); - retVal = _CheckGenericParamInstance(genericParamInst); - if (retVal) - return retVal; + if (genericParamInst->mTypeConstraint == toType) + return typedVal.mValue; } - - // Check method generic constraints - if ((mCurMethodInstance != NULL) && (mCurMethodInstance->mIsUnspecialized) && (mCurMethodInstance->mMethodInfoEx != NULL)) + else { - for (int genericParamIdx = (int)mCurMethodInstance->mMethodInfoEx->mMethodGenericArguments.size(); - genericParamIdx < mCurMethodInstance->mMethodInfoEx->mGenericParams.size(); genericParamIdx++) + if ((typedVal.mKind != Beefy::BfTypedValueKind_GenericConstValue) && (toType == mContext->mBfObjectType)) { - auto genericParamInst = mCurMethodInstance->mMethodInfoEx->mGenericParams[genericParamIdx]; - if (genericParamInst->mExternType == typedVal.mType) + // Always allow casting from generic to object + return typedVal.mValue; + } + + auto _CheckGenericParamInstance = [&](BfGenericParamInstance* genericParamInst) + { + if ((genericParamInst->mGenericParamFlags & BfGenericParamFlag_Var) != 0) { - retVal = _CheckGenericParamInstance(genericParamInst); - if (retVal) - return retVal; + return typedVal.mValue; + } + if (toType->IsInterface()) + { + for (auto iface : genericParamInst->mInterfaceConstraints) + if (TypeIsSubTypeOf(iface, toType->ToTypeInstance())) + return mBfIRBuilder->GetFakeVal(); + } + + if (genericParamInst->mTypeConstraint != NULL) + { + SetAndRestoreValue 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; + } } } } diff --git a/IDEHelper/Tests/src/Generics.bf b/IDEHelper/Tests/src/Generics.bf index 6a3ad18d..e49acc62 100644 --- a/IDEHelper/Tests/src/Generics.bf +++ b/IDEHelper/Tests/src/Generics.bf @@ -241,6 +241,12 @@ namespace Tests return 0; } + static void MethodG() where T : TBase + { + T val = default; + TBase valBase = val; + } + public static TResult Sum(this T it, TDlg dlg) where T: concrete, IEnumerable where TDlg: delegate TResult(TElem) @@ -280,6 +286,16 @@ namespace Tests return .(it.GetEnumerator()); } + class ClassF + { + + } + + class ClassG : ClassF + { + + } + [Test] public static void TestBasics() { @@ -339,6 +355,14 @@ namespace Tests Test.Assert(e.GetNext().Value == 2); Test.Assert(e.GetNext().Value == 4); Test.Assert(e.GetNext().Value == 6); + + Test.Assert( + [IgnoreErrors(true)] + { + MethodG(); + true + } == false); + MethodG(); } }