diff --git a/IDEHelper/Compiler/BfModule.cpp b/IDEHelper/Compiler/BfModule.cpp index 9207ac2c..0017c052 100644 --- a/IDEHelper/Compiler/BfModule.cpp +++ b/IDEHelper/Compiler/BfModule.cpp @@ -2262,47 +2262,65 @@ void BfModule::LocalVariableDone(BfLocalVariable* localVar, bool isMethodExit) { for (auto& fieldInstance : checkTypeInstance->mFieldInstances) { - if (fieldInstance.mMergedDataIdx != -1) - { - int checkMask = 1 << fieldInstance.mMergedDataIdx; - if ((localVar->mUnassignedFieldFlags & checkMask) != 0) - { - auto fieldDef = fieldInstance.GetFieldDef(); - if (auto propertyDeclaration = BfNodeDynCast(fieldDef->mFieldDeclaration)) - { - String propName; - if (propertyDeclaration->mNameNode != NULL) - propertyDeclaration->mNameNode->ToString(propName); + if (fieldInstance.mMergedDataIdx == -1) + continue; - if (checkTypeInstance == mCurTypeInstance) - { - Fail(StrFormat("Auto-implemented property '%s' must be fully assigned before control is returned to the caller", - propName.c_str()), localNameNode, deferFullAnalysis); // 0171 - } - else - { - Fail(StrFormat("Auto-implemented property '%s.%s' must be fully assigned before control is returned to the caller", - TypeToString(checkTypeInstance).c_str(), - propName.c_str()), localNameNode, deferFullAnalysis); // 0171 - } + int checkMask = 1 << fieldInstance.mMergedDataIdx; + if ((localVar->mUnassignedFieldFlags & checkMask) != 0) + { + auto fieldDef = fieldInstance.GetFieldDef(); + + if (mCurMethodInstance->mMethodDef->mDeclaringType->mIsPartial) + { + if (mCurMethodInstance->mMethodDef->mDeclaringType != fieldInstance.GetFieldDef()->mDeclaringType) + { + // This extension is only responsible for its own fields + foundFields = true; + continue; + } + + if ((fieldDef->mFieldDeclaration != NULL) && (fieldDef->mFieldDeclaration->mInitializer != NULL)) + { + // This initializer was handled in CtorNoBody + foundFields = true; + continue; + } + } + + if (auto propertyDeclaration = BfNodeDynCast(fieldDef->mFieldDeclaration)) + { + String propName; + if (propertyDeclaration->mNameNode != NULL) + propertyDeclaration->mNameNode->ToString(propName); + + if (checkTypeInstance == mCurTypeInstance) + { + Fail(StrFormat("Auto-implemented property '%s' must be fully assigned before control is returned to the caller", + propName.c_str()), localNameNode, deferFullAnalysis); // 0171 } else { - if (checkTypeInstance == mCurTypeInstance) - { - Fail(StrFormat("Field '%s' must be fully assigned before control is returned to the caller", - fieldDef->mName.c_str()), localNameNode, deferFullAnalysis); // 0171 - } - else - { - Fail(StrFormat("Field '%s.%s' must be fully assigned before control is returned to the caller", - TypeToString(checkTypeInstance).c_str(), - fieldDef->mName.c_str()), localNameNode, deferFullAnalysis); // 0171 - } + Fail(StrFormat("Auto-implemented property '%s.%s' must be fully assigned before control is returned to the caller", + TypeToString(checkTypeInstance).c_str(), + propName.c_str()), localNameNode, deferFullAnalysis); // 0171 } + } + else + { + if (checkTypeInstance == mCurTypeInstance) + { + Fail(StrFormat("Field '%s' must be fully assigned before control is returned to the caller", + fieldDef->mName.c_str()), localNameNode, deferFullAnalysis); // 0171 + } + else + { + Fail(StrFormat("Field '%s.%s' must be fully assigned before control is returned to the caller", + TypeToString(checkTypeInstance).c_str(), + fieldDef->mName.c_str()), localNameNode, deferFullAnalysis); // 0171 + } + } - foundFields = true; - } + foundFields = true; } } checkTypeInstance = checkTypeInstance->mBaseType; @@ -10087,8 +10105,10 @@ void BfModule::EmitDynamicCastCheck(BfTypedValue typedVal, BfType* type, bool al } } -BfTypedValue BfModule::BoxValue(BfAstNode* srcNode, BfTypedValue typedVal, BfType* toType, const BfAllocTarget& allocTarget, bool callDtor) +BfTypedValue BfModule::BoxValue(BfAstNode* srcNode, BfTypedValue typedVal, BfType* toType, const BfAllocTarget& allocTarget, BfCastFlags castFlags) { + bool callDtor = (castFlags & BfCastFlags_NoBoxDtor) == 0; + if (mBfIRBuilder->mIgnoreWrites) { if (toType == mContext->mBfObjectType) @@ -10160,7 +10180,7 @@ BfTypedValue BfModule::BoxValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp loadedVal = mBfIRBuilder->CreateLoad(nullableValueAddr); } - auto boxedVal = BoxValue(srcNode, BfTypedValue(loadedVal, fromStructTypeInstance->GetUnderlyingType()), resultType, allocTarget, callDtor); + auto boxedVal = BoxValue(srcNode, BfTypedValue(loadedVal, fromStructTypeInstance->GetUnderlyingType()), resultType, allocTarget, callDtor ? BfCastFlags_None : BfCastFlags_NoBoxDtor); RestoreScopeState(); if (!boxedVal) return BfTypedValue(); @@ -17067,7 +17087,7 @@ void BfModule::EmitCtorBody(bool& skipBody) } // Zero out memory for default ctor - if ((methodDeclaration == NULL) && (mCurTypeInstance->IsStruct())) + if ((methodDeclaration == NULL) && (mCurTypeInstance->IsStruct()) && (methodInstance->mChainType != BfMethodChainType_ChainMember)) { if (mCurTypeInstance->IsTypedPrimitive()) { @@ -17075,7 +17095,7 @@ void BfModule::EmitCtorBody(bool& skipBody) mBfIRBuilder->CreateStore(GetDefaultValue(mCurTypeInstance), thisRef); } else if (mCurTypeInstance->mInstSize > 0) - { + { BfIRValue fillValue = GetConstValue8(0); BfIRValue sizeValue = GetConstValue(mCurTypeInstance->mInstSize); auto thisRef = mCurMethodState->mLocals[0]->mValue; diff --git a/IDEHelper/Compiler/BfModule.h b/IDEHelper/Compiler/BfModule.h index aa80411d..1f77b859 100644 --- a/IDEHelper/Compiler/BfModule.h +++ b/IDEHelper/Compiler/BfModule.h @@ -1626,7 +1626,7 @@ public: void EmitDeferredCallProcessor(SLIList& callEntries, BfIRValue callTail); bool CanCast(BfTypedValue typedVal, BfType* toType, BfCastFlags castFlags = BfCastFlags_None); bool AreSplatsCompatible(BfType* fromType, BfType* toType, bool* outNeedsMemberCasting); - BfTypedValue BoxValue(BfAstNode* srcNode, BfTypedValue typedVal, BfType* toType /*Can be System.Object or interface*/, const BfAllocTarget& allocTarget, bool callDtor = true); + BfTypedValue BoxValue(BfAstNode* srcNode, BfTypedValue typedVal, BfType* toType /*Can be System.Object or interface*/, const BfAllocTarget& allocTarget, BfCastFlags castFlags = BfCastFlags_None); BfIRValue CastToFunction(BfAstNode* srcNode, const BfTypedValue& targetValue, BfMethodInstance* methodInstance, BfType* toType, BfCastFlags castFlags = BfCastFlags_None, BfIRValue irFunc = BfIRValue()); BfIRValue CastToValue(BfAstNode* srcNode, BfTypedValue val, BfType* toType, BfCastFlags castFlags = BfCastFlags_None, BfCastResultFlags* resultFlags = NULL); BfTypedValue Cast(BfAstNode* srcNode, const BfTypedValue& val, BfType* toType, BfCastFlags castFlags = BfCastFlags_None); diff --git a/IDEHelper/Compiler/BfModuleTypeUtils.cpp b/IDEHelper/Compiler/BfModuleTypeUtils.cpp index 4074bd20..39b83db0 100644 --- a/IDEHelper/Compiler/BfModuleTypeUtils.cpp +++ b/IDEHelper/Compiler/BfModuleTypeUtils.cpp @@ -11906,7 +11906,7 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp // Null -> ObjectInst|IFace|ptr if ((typedVal.mType->IsNull()) && - ((toType->IsObjectOrInterface()) || (toType->IsPointer() || (toType->IsFunction())))) + ((toType->IsObjectOrInterface()) || (toType->IsPointer() || (toType->IsFunction()) || (toType->IsAllocType())))) { return mBfIRBuilder->CreateBitCast(typedVal.mValue, mBfIRBuilder->MapType(toType)); } @@ -13274,7 +13274,7 @@ BfIRValue BfModule::CastToValue(BfAstNode* srcNode, BfTypedValue typedVal, BfTyp } SetAndRestoreValue prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, ignoreWrites); - auto value = BoxValue(srcNode, typedVal, toType, scopeData, (castFlags & BfCastFlags_NoBoxDtor) == 0); + auto value = BoxValue(srcNode, typedVal, toType, scopeData, castFlags); if (value) return value.mValue; } diff --git a/IDEHelper/Tests/src/Extensions.bf b/IDEHelper/Tests/src/Extensions.bf index 63e5a5d0..a2974ad8 100644 --- a/IDEHelper/Tests/src/Extensions.bf +++ b/IDEHelper/Tests/src/Extensions.bf @@ -237,6 +237,28 @@ namespace Tests return val.GetIt().GetExVal(); } + struct StructA + { + public int mVal; + public int mVal2 = 111; + + public this(int val, int val2) + { + mVal = val; + mVal2 = val2; + } + } + + extension StructA + { + public int mVal3 = 222; + + public this(int val) + { + mVal = val; + } + } + [Test] public static void TestBasics() { @@ -272,6 +294,11 @@ namespace Tests obj = new ClassG(); delete obj; Test.Assert(ClassF.sVal == 6543); + + StructA ms = .(1); + Test.Assert((ms.mVal == 1) && (ms.mVal2 == 111) && (ms.mVal3 == 222)); + ms = .(1, 2); + Test.Assert((ms.mVal == 1) && (ms.mVal2 == 2) && (ms.mVal3 == 222)); } [Test]