diff --git a/BeefLibs/corlib/src/GC.bf b/BeefLibs/corlib/src/GC.bf index f8c0e176..d3f67ad8 100644 --- a/BeefLibs/corlib/src/GC.bf +++ b/BeefLibs/corlib/src/GC.bf @@ -149,6 +149,19 @@ namespace System public static void ExcludeThreadId(int thereadId) {} #endif + static void MarkAppendedObject(Object obj) + { +#if BF_ENABLE_REALTIME_LEAK_CHECK + ClassVData* maskedVData = (ClassVData*)(void*)(obj.[Friend]mClassVData & ~(int)0xFF); + if (maskedVData == null) + return; +#else + if (obj.mClassVData == null) + return; +#endif + obj.[Friend]GCMarkMembers(); + } + static void MarkDerefedObject(Object* obj) { #if BF_ENABLE_REALTIME_LEAK_CHECK diff --git a/BeefLibs/corlib/src/Internal.bf b/BeefLibs/corlib/src/Internal.bf index 26b1f49a..baa46b22 100644 --- a/BeefLibs/corlib/src/Internal.bf +++ b/BeefLibs/corlib/src/Internal.bf @@ -89,6 +89,8 @@ namespace System [CallingConvention(.Cdecl), NoReturn] public static extern void ThrowIndexOutOfRange(int stackOffset = 0); [CallingConvention(.Cdecl), NoReturn] + public static extern void ThrowObjectNotInitialized(int stackOffset = 0); + [CallingConvention(.Cdecl), NoReturn] public static extern void FatalError(String error, int stackOffset = 0); [Intrinsic("memcpy")] public static extern void MemCpy(void* dest, void* src, int length, int32 align = 1, bool isVolatile = false); diff --git a/BeefLibs/corlib/src/Reflection/FieldInfo.bf b/BeefLibs/corlib/src/Reflection/FieldInfo.bf index c08e08f2..e3066926 100644 --- a/BeefLibs/corlib/src/Reflection/FieldInfo.bf +++ b/BeefLibs/corlib/src/Reflection/FieldInfo.bf @@ -8,7 +8,8 @@ namespace System.Reflection public enum Error { InvalidTargetType, - InvalidValueType + InvalidValueType, + AppendedField } TypeInstance mTypeInstance; @@ -24,6 +25,7 @@ namespace System.Reflection public int32 MemberOffset => (int32)mFieldData.mData; public Type FieldType => Type.[Friend]GetType(mFieldData.mFieldTypeId); public bool IsConst => mFieldData.mFlags.HasFlag(.Const); + public bool IsAppended => mFieldData.mFlags.HasFlag(.Appended); public bool IsEnumCase => mFieldData.mFlags.HasFlag(.EnumCase); public bool IsReadOnly => mFieldData.mFlags.HasFlag(.ReadOnly); public bool IsStatic => mFieldData.mFlags.HasFlag(.Static); @@ -86,7 +88,12 @@ namespace System.Reflection if (valueType == fieldType) { if (valueType.IsObject) + { + if (mFieldData.mFlags.HasFlag(.Appended)) + return .Err(.AppendedField); + *((void**)dataAddr) = Internal.UnsafeCastToPtr(value); + } else Internal.MemCpy(dataAddr, valueDataAddr, fieldType.[Friend]mSize); } @@ -385,7 +392,10 @@ namespace System.Reflection if (typeCode == TypeCode.Object) { value.[Friend]mStructType = 0; - value.[Friend]mData = *(int*)targetDataAddr; + if (mFieldData.mFlags.HasFlag(.Appended)) + value.[Friend]mData = (int)targetDataAddr; + else + value.[Friend]mData = *(int*)targetDataAddr; } else { diff --git a/BeefLibs/corlib/src/Type.bf b/BeefLibs/corlib/src/Type.bf index a36f5eb0..0420a43d 100644 --- a/BeefLibs/corlib/src/Type.bf +++ b/BeefLibs/corlib/src/Type.bf @@ -1480,6 +1480,7 @@ namespace System.Reflection EnumDiscriminator = 0x0200, EnumCase = 0x0400, ReadOnly = 0x0800, + Appended = 0x1000, } public enum MethodFlags : uint16 diff --git a/BeefRT/rt/Internal.cpp b/BeefRT/rt/Internal.cpp index 4bcc49a4..8d36ebe4 100644 --- a/BeefRT/rt/Internal.cpp +++ b/BeefRT/rt/Internal.cpp @@ -86,6 +86,7 @@ namespace bf BFRT_EXPORT static void ObjectDynCheck(Object* object, int typeId, bool allowNull); BFRT_EXPORT static void ObjectDynCheckFailed(Object* object, int typeId); BFRT_EXPORT static void ThrowIndexOutOfRange(intptr stackOffset); + BFRT_EXPORT static void ThrowObjectNotInitialized(intptr stackOffset); BFRT_EXPORT static void FatalError(String* error, intptr stackOffset = 0); BFRT_EXPORT static void MemCpy(void* dest, void* src, intptr length); BFRT_EXPORT static void MemMove(void* dest, void* src, intptr length); @@ -423,6 +424,30 @@ void Internal::ThrowIndexOutOfRange(intptr stackOffset) Internal_FatalError("Index out of range"); } +void Internal::ThrowObjectNotInitialized(intptr stackOffset) +{ + if (gClientPipe != NULL) + { + if (gTestBreakOnFailure) + { + SETUP_ERROR("Object not initialized", (int)(2 + stackOffset)); + BF_DEBUG_BREAK(); + } + + Beefy::String str = ":TestFail\tObject not initialized\n"; + TestString(str); + exit(1); + } + + if ((stackOffset != -1) && (::IsDebuggerPresent())) + { + SETUP_ERROR("Object not initialized", (int)(2 + stackOffset)); + BF_DEBUG_BREAK(); + } + + Internal_FatalError("Object not initialized"); +} + void Internal::FatalError(bf::System::String* error, intptr stackOffset) { if (gClientPipe != NULL) diff --git a/IDE/src/ui/SourceEditWidgetContent.bf b/IDE/src/ui/SourceEditWidgetContent.bf index cc723bf7..a64f91ae 100644 --- a/IDE/src/ui/SourceEditWidgetContent.bf +++ b/IDE/src/ui/SourceEditWidgetContent.bf @@ -4942,6 +4942,10 @@ namespace IDE.ui menuItem.SetDisabled(!isPaused); menuItem.mOnMenuItemSelected.Add(new (evt) => IDEApp.sApp.ShowDisassemblyAtCursor()); + menuItem = menu.AddItem("Set Next Statement"); + menuItem.SetDisabled(!isPaused); + menuItem.mOnMenuItemSelected.Add(new (evt) => IDEApp.sApp.[Friend]SetNextStatement()); + var stepIntoSpecificMenu = menu.AddItem("Step into Specific"); stepIntoSpecificMenu.SetDisabled(!isPaused); stepIntoSpecificMenu.IsParent = true; diff --git a/IDEHelper/Compiler/BfAst.h b/IDEHelper/Compiler/BfAst.h index b9c528fb..ef5e3050 100644 --- a/IDEHelper/Compiler/BfAst.h +++ b/IDEHelper/Compiler/BfAst.h @@ -3169,7 +3169,7 @@ public: BfAstNode* mConstSpecifier; // Could be 'const' or 'using' BfTokenNode* mVolatileSpecifier; BfTokenNode* mNewSpecifier; - BfTokenNode* mExternSpecifier; + BfTokenNode* mExternSpecifier; // Could be 'extern' or 'append' BfTypeReference* mTypeRef; BfIdentifierNode* mNameNode; BfTokenNode* mEqualsNode; diff --git a/IDEHelper/Compiler/BfAutoComplete.cpp b/IDEHelper/Compiler/BfAutoComplete.cpp index f168ae08..93a53cb0 100644 --- a/IDEHelper/Compiler/BfAutoComplete.cpp +++ b/IDEHelper/Compiler/BfAutoComplete.cpp @@ -1741,7 +1741,7 @@ void BfAutoComplete::CheckIdentifier(BfAstNode* identifierNode, bool isInExpress { const char* tokens [] = { - "alignof", "as", "asm", "base", "break", "case", "catch", "checked", "continue", "const", "default", "defer", + "alignof", "append", "as", "asm", "base", "break", "case", "catch", "checked", "continue", "const", "default", "defer", "delegate", "delete", "do", "else", "false", "finally", "fixed", "for", "function", "if", "implicit", "in", "internal", "is", "isconst", "new", "mixin", "null", "offsetof", "out", "params", "readonly", "ref", "rettype", "return", @@ -1762,7 +1762,7 @@ void BfAutoComplete::CheckIdentifier(BfAstNode* identifierNode, bool isInExpress { const char* tokens[] = { - "abstract", "base", "class", "const", + "abstract", "append", "base", "class", "const", "delegate", "extern", "enum", "explicit", "extension", "function", "interface", "in", "implicit", "internal", "mixin", "namespace", "new", "operator", "out", "override", "params", "private", "protected", "public", "readonly", "ref", "rettype", "return", diff --git a/IDEHelper/Compiler/BfDefBuilder.cpp b/IDEHelper/Compiler/BfDefBuilder.cpp index c77f509e..7451842e 100644 --- a/IDEHelper/Compiler/BfDefBuilder.cpp +++ b/IDEHelper/Compiler/BfDefBuilder.cpp @@ -1184,7 +1184,8 @@ void BfDefBuilder::Visit(BfFieldDeclaration* fieldDeclaration) fieldDef->mProtection = BfProtection_Public; fieldDef->mIsReadOnly = fieldDeclaration->mReadOnlySpecifier != NULL; fieldDef->mIsInline = (fieldDeclaration->mReadOnlySpecifier != NULL) && (fieldDeclaration->mReadOnlySpecifier->GetToken() == BfToken_Inline); - fieldDef->mIsExtern = (fieldDeclaration->mExternSpecifier != NULL); + fieldDef->mIsExtern = (fieldDeclaration->mExternSpecifier != NULL) && (fieldDeclaration->mExternSpecifier->mToken == BfToken_Extern); + fieldDef->mIsAppend = (fieldDeclaration->mExternSpecifier != NULL) && (fieldDeclaration->mExternSpecifier->mToken == BfToken_Append); auto constSpecifierToken = BfNodeDynCast(fieldDeclaration->mConstSpecifier); fieldDef->mIsConst = ((constSpecifierToken != NULL) && (constSpecifierToken->mToken == BfToken_Const)) || (isEnumEntryDecl); if (auto usingSpecifier = BfNodeDynCast(fieldDeclaration->mConstSpecifier)) @@ -2197,6 +2198,11 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) } if (field->GetFieldDeclaration()->mFieldDtor != NULL) needsStaticDtor = true; + if (field->mIsAppend) + { + needsStaticInit = true; + needsStaticDtor = true; + } } } @@ -2222,6 +2228,11 @@ void BfDefBuilder::FinishTypeDef(bool wantsToString) hasNonStaticField = true; if (field->GetInitializer() != NULL) needsDefaultCtor = true; + if (field->mIsAppend) + { + needsDefaultCtor = true; + needsDtor = true; + } if (auto fieldDecl = field->GetFieldDeclaration()) { if (fieldDecl->mFieldDtor != NULL) diff --git a/IDEHelper/Compiler/BfExprEvaluator.cpp b/IDEHelper/Compiler/BfExprEvaluator.cpp index 90633939..7c423066 100644 --- a/IDEHelper/Compiler/BfExprEvaluator.cpp +++ b/IDEHelper/Compiler/BfExprEvaluator.cpp @@ -5008,6 +5008,42 @@ BfTypedValue BfExprEvaluator::LoadField(BfAstNode* targetSrc, BfTypedValue targe bool isStaticCtor = (mModule->mCurMethodInstance != NULL) && (mModule->mCurMethodInstance->mMethodDef->IsCtorOrInit()) && (mModule->mCurMethodInstance->mMethodDef->mIsStatic); + + if ((mModule->mCompiler->mOptions.mRuntimeChecks) && (fieldInstance->IsAppendedObject()) && (!mModule->mBfIRBuilder->mIgnoreWrites) && + (!mModule->IsSkippingExtraResolveChecks())) + { + auto intType = mModule->GetPrimitiveType(BfTypeCode_IntPtr); + auto intPtrType = mModule->CreatePointerType(intType); + auto intPtrVal = mModule->mBfIRBuilder->CreateBitCast(retVal.mValue, mModule->mBfIRBuilder->MapType(intPtrType)); + auto intVal = mModule->mBfIRBuilder->CreateLoad(intPtrVal); + + auto oobBlock = mModule->mBfIRBuilder->CreateBlock("oob", true); + auto contBlock = mModule->mBfIRBuilder->CreateBlock("cont", true); + + auto cmpRes = mModule->mBfIRBuilder->CreateCmpEQ(intVal, mModule->mBfIRBuilder->CreateConst(BfTypeCode_IntPtr, 0)); + mModule->mBfIRBuilder->CreateCondBr(cmpRes, oobBlock, contBlock); + + mModule->mBfIRBuilder->SetInsertPoint(oobBlock); + auto internalType = mModule->ResolveTypeDef(mModule->mCompiler->mInternalTypeDef); + auto oobFunc = mModule->GetMethodByName(internalType->ToTypeInstance(), "ThrowObjectNotInitialized"); + if (oobFunc.mFunc) + { + if (mModule->mIsComptimeModule) + mModule->mCompiler->mCeMachine->QueueMethod(oobFunc.mMethodInstance, oobFunc.mFunc); + + SizedArray args; + args.push_back(mModule->GetConstValue(0)); + mModule->mBfIRBuilder->CreateCall(oobFunc.mFunc, args); + mModule->mBfIRBuilder->CreateUnreachable(); + } + else + { + mModule->Fail("System.Internal class must contain method 'ThrowObjectNotInitialized'", fieldDef->GetRefNode()); + } + + mModule->mBfIRBuilder->SetInsertPoint(contBlock); + } + if ((fieldDef->mIsReadOnly) && (!isStaticCtor)) { if (retVal.IsAddr()) @@ -5135,8 +5171,16 @@ BfTypedValue BfExprEvaluator::LoadField(BfAstNode* targetSrc, BfTypedValue targe if ((targetValue.IsAddr()) && (!typeInstance->IsValueType())) targetValue = mModule->LoadValue(targetValue); - retVal = BfTypedValue(mModule->mBfIRBuilder->CreateInBoundsGEP(targetValue.mValue, 0, fieldInstance->mDataIdx/*, fieldDef->mName*/), - resolvedFieldType, target.IsReadOnly() ? BfTypedValueKind_ReadOnlyAddr : BfTypedValueKind_Addr); + if (fieldInstance->IsAppendedObject()) + { + auto elemPtr = mModule->mBfIRBuilder->CreateInBoundsGEP(targetValue.mValue, 0, fieldInstance->mDataIdx); + retVal = BfTypedValue(mModule->mBfIRBuilder->CreateBitCast(elemPtr, mModule->mBfIRBuilder->MapType(resolvedFieldType)), resolvedFieldType); + } + else + { + retVal = BfTypedValue(mModule->mBfIRBuilder->CreateInBoundsGEP(targetValue.mValue, 0, fieldInstance->mDataIdx/*, fieldDef->mName*/), + resolvedFieldType, target.IsReadOnly() ? BfTypedValueKind_ReadOnlyAddr : BfTypedValueKind_Addr); + } } if (!retVal.IsSplat()) @@ -9649,15 +9693,24 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp if (resolvedTypeInstance != NULL) { - if ((!resolvedTypeInstance->IsStruct()) && (!resolvedTypeInstance->IsTypedPrimitive())) + if ((mBfEvalExprFlags & BfEvalExprFlags_AppendFieldInitializer) == 0) { - if (mModule->PreFail()) - mModule->Fail("Objects must be allocated through 'new' or 'scope'", targetSrc); - return BfTypedValue(); + if ((!resolvedTypeInstance->IsStruct()) && (!resolvedTypeInstance->IsTypedPrimitive())) + { + if (mModule->PreFail()) + mModule->Fail("Objects must be allocated through 'new' or 'scope'", targetSrc); + return BfTypedValue(); + } } if (auto identifier = BfNodeDynCastExact(targetSrc)) - mModule->SetElementType(identifier, resolvedTypeInstance->IsEnum() ? BfSourceElementType_Type : BfSourceElementType_Struct); + { + auto elementType = resolvedTypeInstance->IsEnum() ? BfSourceElementType_Type : BfSourceElementType_Struct; + if (resolvedTypeInstance->IsObject()) + elementType = BfSourceElementType_RefType; + mModule->SetElementType(identifier, elementType); + } + if (mModule->mCompiler->mResolvePassData != NULL) { if (!BfNodeIsA(targetSrc)) @@ -9688,7 +9741,7 @@ BfTypedValue BfExprEvaluator::MatchMethod(BfAstNode* targetSrc, BfMethodBoundExp mResultLocalVar = NULL; mResultFieldInstance = NULL; mResultLocalVarRefNode = NULL; - auto result = MatchConstructor(targetSrc, methodBoundExpr, structInst, resolvedTypeInstance, argValues, false, false); + auto result = MatchConstructor(targetSrc, methodBoundExpr, structInst, resolvedTypeInstance, argValues, false, resolvedTypeInstance->IsObject()); if ((result) && (!result.mType->IsVoid())) return result; mModule->ValidateAllocation(resolvedTypeInstance, targetSrc); @@ -17564,7 +17617,7 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m else mResult = BfTypedValue(mModule->CreateAlloca(expectingType), expectingType, BfTypedValueKind_TempAddr); - auto ctorResult = MatchConstructor(target, methodBoundExpr, mResult, expectingType->ToTypeInstance(), argValues, false, false); + auto ctorResult = MatchConstructor(target, methodBoundExpr, mResult, expectingType->ToTypeInstance(), argValues, false, false); if ((ctorResult) && (!ctorResult.mType->IsVoid())) mResult = ctorResult; mModule->ValidateAllocation(expectingType, invocationExpr->mTarget); @@ -17599,7 +17652,7 @@ void BfExprEvaluator::DoInvocation(BfAstNode* target, BfMethodBoundExpression* m { // Allow } - else + else if ((mBfEvalExprFlags & BfEvalExprFlags_AppendFieldInitializer) == 0) { gaveUnqualifiedDotError = true; if (mModule->PreFail()) @@ -21419,25 +21472,20 @@ void BfExprEvaluator::Visit(BfIndexerExpression* indexerExpr) auto oobFunc = mModule->GetMethodByName(internalType->ToTypeInstance(), "ThrowIndexOutOfRange"); if (oobFunc.mFunc) { - /*if (!mModule->mCompiler->mIsResolveOnly) - { - OutputDebugStrF("-OOB %d %d\n", oobFunc.mFunc.mId, oobFunc.mFunc.mFlags); - }*/ - if (mModule->mIsComptimeModule) mModule->mCompiler->mCeMachine->QueueMethod(oobFunc.mMethodInstance, oobFunc.mFunc); SizedArray args; args.push_back(mModule->GetConstValue(0)); mModule->mBfIRBuilder->CreateCall(oobFunc.mFunc, args); - mModule->mBfIRBuilder->CreateUnreachable(); - - mModule->mBfIRBuilder->SetInsertPoint(contBlock); + mModule->mBfIRBuilder->CreateUnreachable(); } else { mModule->Fail("System.Internal class must contain method 'ThrowIndexOutOfRange'"); } + + mModule->mBfIRBuilder->SetInsertPoint(contBlock); } } diff --git a/IDEHelper/Compiler/BfIRBuilder.cpp b/IDEHelper/Compiler/BfIRBuilder.cpp index f5149110..fdb63f56 100644 --- a/IDEHelper/Compiler/BfIRBuilder.cpp +++ b/IDEHelper/Compiler/BfIRBuilder.cpp @@ -3203,6 +3203,9 @@ void BfIRBuilder::CreateDbgTypeDefinition(BfType* type) PopulateType(resolvedFieldType, BfIRPopulateType_Eventually_Full); resolvedFieldDIType = DbgGetType(resolvedFieldType); + if (fieldInstance->IsAppendedObject()) + resolvedFieldDIType = DbgGetTypeInst(resolvedFieldType->ToTypeInstance()); + if ((fieldDef == NULL) && (typeInstance->IsPayloadEnum())) { orderedFields.push_back(fieldInstance); @@ -3409,7 +3412,7 @@ void BfIRBuilder::CreateDbgTypeDefinition(BfType* type) if (fieldDef->mHasMultiDefs) fieldName += "$" + fieldDef->mDeclaringType->mProject->mName; auto memberType = DbgCreateMemberType(diForwardDecl, fieldName, fileDIScope, lineNum, - resolvedFieldType->mSize * 8, resolvedFieldType->mAlign * 8, fieldInstance->mDataOffset * 8, + fieldInstance->mDataSize * 8, resolvedFieldType->mAlign * 8, fieldInstance->mDataOffset * 8, flags, resolvedFieldDIType); diFieldTypes.push_back(memberType); } @@ -3712,6 +3715,9 @@ void BfIRBuilder::CreateTypeDefinition_Data(BfModule* populateModule, BfTypeInst if ((fieldDef != NULL) && (resolvedFieldType->IsStruct())) PopulateType(resolvedFieldType, BfIRPopulateType_Eventually_Full); + if (fieldInstance->IsAppendedObject()) + PopulateType(resolvedFieldType, BfIRPopulateType_Eventually_Full); + if ((fieldDef == NULL) && (typeInstance->IsPayloadEnum())) { orderedFields.push_back(fieldInstance); @@ -3761,10 +3767,27 @@ void BfIRBuilder::CreateTypeDefinition_Data(BfModule* populateModule, BfTypeInst if (fieldInstance == NULL) continue; + auto fieldDef = fieldInstance->GetFieldDef(); + auto resolvedFieldType = fieldInstance->GetResolvedType(); BfIRType resolvedFieldIRType = MapType(resolvedFieldType); + if (fieldInstance->IsAppendedObject()) + { + auto fieldTypeInst = fieldInstance->mResolvedType->ToTypeInstance(); + + if (fieldInstance->mDataSize != fieldTypeInst->mInstSize) + { + SizedArray types; + types.push_back(MapTypeInst(fieldTypeInst)); + types.push_back(GetSizedArrayType(GetPrimitiveType(BfTypeCode_Int8), fieldInstance->mDataSize - fieldTypeInst->mInstSize)); + resolvedFieldIRType = CreateStructType(types); + } + else + resolvedFieldIRType = MapTypeInst(fieldTypeInst); + } + if (fieldInstance->mDataOffset > dataPos) { int fillSize = fieldInstance->mDataOffset - dataPos; diff --git a/IDEHelper/Compiler/BfModule.cpp b/IDEHelper/Compiler/BfModule.cpp index e697814c..7a996329 100644 --- a/IDEHelper/Compiler/BfModule.cpp +++ b/IDEHelper/Compiler/BfModule.cpp @@ -4048,8 +4048,18 @@ void BfModule::CreateStaticField(BfFieldInstance* fieldInstance, bool isThreadLo BfMangler::Mangle(staticVarName, mCompiler->GetMangleKind(), fieldInstance); if ((!fieldType->IsValuelessType()) && (!staticVarName.StartsWith("#"))) { + BfIRType irType; + + if (fieldInstance->IsAppendedObject()) + { + irType = mBfIRBuilder->MapTypeInst(fieldType->ToTypeInstance(), BfIRPopulateType_Eventually_Full); + initValue = mBfIRBuilder->CreateConstAggZero(irType); + } + else + irType = mBfIRBuilder->MapType(fieldType, BfIRPopulateType_Eventually_Full); + BfIRValue globalVar = mBfIRBuilder->CreateGlobalVariable( - mBfIRBuilder->MapType(fieldType, BfIRPopulateType_Eventually_Full), + irType, false, BfIRLinkageType_External, initValue, @@ -4551,6 +4561,83 @@ BfTypedValue BfModule::GetFieldInitializerValue(BfFieldInstance* fieldInstance, return result; } +void BfModule::AppendedObjectInit(BfFieldInstance* fieldInst) +{ + BfExprEvaluator exprEvaluator(this); + + bool failed = false; + + auto fieldDef = fieldInst->GetFieldDef(); + auto initializer = fieldDef->GetInitializer(); + + BfResolvedArgs resolvedArgs; + if (auto invocationExpr = BfNodeDynCast(initializer)) + { + bool isDot = false; + + if (auto memberRefExpr = BfNodeDynCast(invocationExpr->mTarget)) + isDot = (memberRefExpr->mTarget == NULL) && (memberRefExpr->mMemberName == NULL); + + if (!isDot) + { + auto resolvedType = ResolveTypeRef(invocationExpr->mTarget, {}); + if ((resolvedType == NULL) || (resolvedType != fieldInst->mResolvedType)) + failed = true; + } + + SetAndRestoreValue prevExpectingType(exprEvaluator.mExpectingType, fieldInst->mResolvedType); + + resolvedArgs.Init(invocationExpr->mOpenParen, &invocationExpr->mArguments, &invocationExpr->mCommas, invocationExpr->mCloseParen); + exprEvaluator.ResolveArgValues(resolvedArgs, BfResolveArgsFlag_DeferParamEval); + } + else if (initializer != NULL) + { + GetFieldInitializerValue(fieldInst); + failed = true; + } + + if (failed) + Fail("Append fields can only be initialized with a call to their constructor", initializer); + + auto intType = GetPrimitiveType(BfTypeCode_IntPtr); + auto int8Type = mBfIRBuilder->GetPrimitiveType(BfTypeCode_Int8); + auto ptrType = mBfIRBuilder->GetPointerTo(int8Type); + auto ptrPtrType = mBfIRBuilder->GetPointerTo(ptrType); + + + BfIRValue fieldAddr; + if (fieldDef->mIsStatic) + { + fieldAddr = ReferenceStaticField(fieldInst).mValue; + } + else + fieldAddr = mBfIRBuilder->CreateInBoundsGEP(mCurMethodState->mLocals[0]->mValue, 0, fieldInst->mDataIdx); + auto thisValue = BfTypedValue(mBfIRBuilder->CreateBitCast(fieldAddr, mBfIRBuilder->MapType(fieldInst->mResolvedType)), fieldInst->mResolvedType); + + auto indexVal = BfTypedValue(CreateAlloca(intType), CreateRefType(intType)); + auto intThisVal = mBfIRBuilder->CreatePtrToInt(thisValue.mValue, (intType->mSize == 4) ? BfTypeCode_Int32 : BfTypeCode_Int64); + mBfIRBuilder->CreateStore(intThisVal, indexVal.mValue); + + auto ctorResult = exprEvaluator.MatchConstructor(fieldDef->GetNameNode(), NULL, thisValue, fieldInst->mResolvedType->ToTypeInstance(), resolvedArgs, false, true, &indexVal); + + auto vObjectAddr = mBfIRBuilder->CreateInBoundsGEP(thisValue.mValue, 0, 0); + + auto vDataRef = CreateClassVDataGlobal(fieldInst->mResolvedType->ToTypeInstance()); + + auto destAddr = mBfIRBuilder->CreateBitCast(vObjectAddr, ptrPtrType); + auto srcVal = mBfIRBuilder->CreateBitCast(vDataRef, ptrType); + mBfIRBuilder->CreateStore(srcVal, destAddr); + + if ((mCompiler->mOptions.mObjectHasDebugFlags) && (!mIsComptimeModule)) + { + auto int8Type = mBfIRBuilder->GetPrimitiveType(BfTypeCode_Int8); + auto ptrType = mBfIRBuilder->GetPointerTo(int8Type); + + auto thisFlagsPtr = mBfIRBuilder->CreateBitCast(thisValue.mValue, ptrType); + mBfIRBuilder->CreateStore(GetConstValue8(BfObjectFlag_AppendAlloc), thisFlagsPtr); + } +} + void BfModule::CheckInterfaceMethod(BfMethodInstance* methodInstance) { @@ -5570,7 +5657,9 @@ BfIRValue BfModule::CreateFieldData(BfFieldInstance* fieldInstance, int customAt if (fieldDef->IsEnumCaseEntry()) fieldFlags = (BfFieldFlags)(fieldFlags | BfFieldFlags_EnumCase); if (fieldDef->mIsReadOnly) - fieldFlags = (BfFieldFlags)(fieldFlags | BfFieldFlags_ReadOnly); + fieldFlags = (BfFieldFlags)(fieldFlags | BfFieldFlags_ReadOnly); + if (fieldInstance->IsAppendedObject()) + fieldFlags = (BfFieldFlags)(fieldFlags | BfFieldFlags_Appended); BfIRValue constValue; BfIRValue constValue2; @@ -5608,8 +5697,13 @@ BfIRValue BfModule::CreateFieldData(BfFieldInstance* fieldInstance, int customAt } } - if ((refVal.IsAddr()) && (!isComptimeArg)) - constValue = mBfIRBuilder->CreatePtrToInt(refVal.mValue, BfTypeCode_IntPtr); + if (!isComptimeArg) + { + if (refVal.IsAddr()) + constValue = mBfIRBuilder->CreatePtrToInt(refVal.mValue, BfTypeCode_IntPtr); + else if (fieldInstance->IsAppendedObject()) + constValue = mBfIRBuilder->CreatePtrToInt(refVal.mValue, BfTypeCode_IntPtr); + } } if (!constValue) @@ -11240,7 +11334,7 @@ StringT<128> BfModule::MethodToString(BfMethodInstance* methodInst, BfMethodName for (int paramIdx = 0; paramIdx < (int)methodInst->GetParamCount(); paramIdx++) { int paramKind = methodInst->GetParamKind(paramIdx); - if (paramKind == BfParamKind_ImplicitCapture) + if ((paramKind == BfParamKind_ImplicitCapture) || (paramKind == BfParamKind_AppendIdx)) continue; if (dispParamIdx > 0) @@ -14799,6 +14893,9 @@ BfTypedValue BfModule::ReferenceStaticField(BfFieldInstance* fieldInstance) { BfIRType irType = mBfIRBuilder->MapType(typeType); + if (fieldInstance->IsAppendedObject()) + irType = mBfIRBuilder->MapTypeInst(typeType->ToTypeInstance()); + SetAndRestoreValue prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, mBfIRBuilder->mIgnoreWrites || staticVarName.StartsWith('#')); globalValue = mBfIRBuilder->CreateGlobalVariable( @@ -14822,7 +14919,7 @@ BfTypedValue BfModule::ReferenceStaticField(BfFieldInstance* fieldInstance) if (fieldDef->mIsVolatile) return BfTypedValue(globalValue, type, BfTypedValueKind_VolatileAddr); - return BfTypedValue(globalValue, type, !fieldDef->mIsConst); + return BfTypedValue(globalValue, type, !fieldDef->mIsConst && !fieldInstance->IsAppendedObject()); } BfFieldInstance* BfModule::GetFieldInstance(BfTypeInstance* typeInst, int fieldIdx, const char* fieldName) @@ -16381,13 +16478,13 @@ void BfModule::CalcAppendAlign(BfMethodInstance* methodInst) methodInst->mAppendAllocAlign = 1; } -BfTypedValue BfModule::TryConstCalcAppend(BfMethodInstance* methodInst, SizedArrayImpl& args) +BfTypedValue BfModule::TryConstCalcAppend(BfMethodInstance* methodInst, SizedArrayImpl& args, bool force) { BP_ZONE("BfModule::TryConstCalcAppend"); BF_ASSERT(methodInst->mMethodDef->mMethodType == BfMethodType_CtorCalcAppend); - if ((mCompiler->mIsResolveOnly) && (!mIsComptimeModule)) + if ((mCompiler->mIsResolveOnly) && (!mIsComptimeModule) && (!force)) return BfTypedValue(); // We want to regenerate all ctor calls when the method internals change @@ -16476,6 +16573,7 @@ BfTypedValue BfModule::TryConstCalcAppend(BfMethodInstance* methodInst, SizedArr BfConstResolveState constResolveState; constResolveState.mMethodInstance = methodInst; constResolveState.mPrevConstResolveState = mCurMethodState->mConstResolveState; + constResolveState.mInCalcAppend = true; SetAndRestoreValue ignoreWrites(mBfIRBuilder->mIgnoreWrites, true); BfMethodState methodState; @@ -16630,7 +16728,7 @@ BfTypedValue BfModule::CallBaseCtorCalc(bool constOnly) BF_ASSERT(bindResult.mIRArgs[0].IsFake()); bindResult.mIRArgs.RemoveAt(0); auto calcAppendMethodModule = GetMethodInstanceAtIdx(bindResult.mMethodInstance->GetOwner(), bindResult.mMethodInstance->mMethodDef->mIdx + 1, BF_METHODNAME_CALCAPPEND); - BfTypedValue appendSizeTypedValue = TryConstCalcAppend(calcAppendMethodModule.mMethodInstance, bindResult.mIRArgs); + BfTypedValue appendSizeTypedValue = TryConstCalcAppend(calcAppendMethodModule.mMethodInstance, bindResult.mIRArgs, true); BF_ASSERT(calcAppendMethodModule.mMethodInstance->mAppendAllocAlign >= 0); mCurMethodInstance->mAppendAllocAlign = BF_MAX((int)mCurMethodInstance->mAppendAllocAlign, calcAppendMethodModule.mMethodInstance->mAppendAllocAlign); BF_ASSERT(calcAppendMethodModule.mMethodInstance->mEndingAppendAllocAlign > -1); @@ -16757,7 +16855,10 @@ void BfModule::CreateStaticCtor() { continue; } - GetFieldInitializerValue(fieldInst, NULL, NULL, NULL, true); + if (fieldInst->IsAppendedObject()) + AppendedObjectInit(fieldInst); + else + GetFieldInitializerValue(fieldInst, NULL, NULL, NULL, true); } } @@ -16788,8 +16889,13 @@ void BfModule::CreateStaticCtor() if ((!BfNodeIsA(fieldDef->mTypeRef)) && (!BfNodeIsA(fieldDef->mTypeRef))) { wantType = ResolveTypeRef(fieldDef->mTypeRef, BfPopulateType_Identity, BfResolveTypeRefFlag_AllowInferredSizedArray); - } - CreateValueFromExpression(fieldDef->GetInitializer(), wantType, BfEvalExprFlags_FieldInitializer); + } + + BfEvalExprFlags exprFlags = BfEvalExprFlags_FieldInitializer; + if (fieldDef->mIsAppend) + exprFlags = (BfEvalExprFlags)(exprFlags | BfEvalExprFlags_AppendFieldInitializer); + + CreateValueFromExpression(fieldDef->GetInitializer(), wantType, exprFlags); } } } @@ -16876,6 +16982,66 @@ void BfModule::EmitDtorBody() if (fieldDef != NULL) fieldDecl = fieldDef->GetFieldDeclaration(); + if ((fieldDef != NULL) && (fieldDef->mIsStatic == methodDef->mIsStatic) && (fieldInst->IsAppendedObject())) + { + auto refNode = fieldDef->GetRefNode(); + UpdateSrcPos(refNode); + + auto objectType = mContext->mBfObjectType; + BfTypeInstance* checkTypeInst = mCurTypeInstance->ToTypeInstance(); + + BfTypedValue val; + if (fieldDef->mIsStatic) + val = ReferenceStaticField(fieldInst); + else + { + auto fieldAddr = mBfIRBuilder->CreateInBoundsGEP(mCurMethodState->mLocals[0]->mValue, 0, fieldInst->mDataIdx); + val = BfTypedValue(mBfIRBuilder->CreateBitCast(fieldAddr, mBfIRBuilder->MapType(fieldInst->mResolvedType)), fieldInst->mResolvedType); + } + + bool allowPrivate = checkTypeInst == mCurTypeInstance; + bool allowProtected = allowPrivate || TypeIsSubTypeOf(mCurTypeInstance, checkTypeInst); + while (checkTypeInst != NULL) + { + auto dtorMethodDef = checkTypeInst->mTypeDef->GetMethodByName("~this"); + if (dtorMethodDef) + { + if (!CheckProtection(dtorMethodDef->mProtection, checkTypeInst->mTypeDef, allowProtected, allowPrivate)) + { + auto error = Fail(StrFormat("'%s.~this()' is inaccessible due to its protection level", TypeToString(checkTypeInst).c_str()), refNode); // CS0122 + } + } + checkTypeInst = checkTypeInst->mBaseType; + allowPrivate = false; + } + + // call dtor + BfExprEvaluator expressionEvaluator(this); + PopulateType(val.mType); + PopulateType(objectType, BfPopulateType_DataAndMethods); + + if (objectType->mVirtualMethodTable.size() == 0) + { + if (!mCompiler->IsAutocomplete()) + AssertErrorState(); + } + else if (!IsSkippingExtraResolveChecks()) + { + BfMethodInstance* methodInstance = objectType->mVirtualMethodTable[mCompiler->GetVTableMethodOffset() + 0].mImplementingMethod; + BF_ASSERT(methodInstance->mMethodDef->mName == "~this"); + SizedArray llvmArgs; + llvmArgs.push_back(mBfIRBuilder->CreateBitCast(val.mValue, mBfIRBuilder->MapType(objectType))); + expressionEvaluator.CreateCall(refNode, methodInstance, mBfIRBuilder->GetFakeVal(), false, llvmArgs); + } + + if ((mCompiler->mOptions.mObjectHasDebugFlags) && (!mIsComptimeModule)) + { + auto int8PtrType = CreatePointerType(GetPrimitiveType(BfTypeCode_Int8)); + auto int8PtrVal = mBfIRBuilder->CreateBitCast(val.mValue, mBfIRBuilder->MapType(int8PtrType)); + mBfIRBuilder->CreateStore(GetConstValue8(BfObjectFlag_Deleted), int8PtrVal); + } + } + if ((fieldDef != NULL) && (fieldDef->mIsStatic == methodDef->mIsStatic) && (fieldDecl != NULL) && (fieldDecl->mFieldDtor != NULL)) { if (fieldDef->mDeclaringType != mCurMethodInstance->mMethodDef->mDeclaringType) @@ -17730,6 +17896,13 @@ void BfModule::EmitCtorBody(bool& skipBody) continue; auto initializer = fieldDef->GetInitializer(); + if (fieldInst->IsAppendedObject()) + { + UpdateSrcPos(fieldDef->GetNameNode()); + AppendedObjectInit(fieldInst); + continue; + } + if (initializer == NULL) { continue; @@ -17819,7 +17992,12 @@ void BfModule::EmitCtorBody(bool& skipBody) if ((wantType != NULL) && ((wantType->IsVar()) || (wantType->IsLet()) || (wantType->IsRef()))) wantType = NULL; - CreateValueFromExpression(initializer, wantType, BfEvalExprFlags_FieldInitializer); + + BfEvalExprFlags exprFlags = BfEvalExprFlags_FieldInitializer; + if (fieldDef->mIsAppend) + exprFlags = (BfEvalExprFlags)(exprFlags | BfEvalExprFlags_AppendFieldInitializer); + + CreateValueFromExpression(initializer, wantType, exprFlags); } } @@ -18375,6 +18553,21 @@ void BfModule::EmitIteratorBlock(bool& skipBody) return; } +void BfModule::EmitGCMarkAppended(BfTypedValue markVal) +{ + auto gcType = ResolveTypeDef(mCompiler->mGCTypeDef, BfPopulateType_DataAndMethods); + if (gcType == NULL) + return; + BfModuleMethodInstance markFromGCThreadMethodInstance = GetMethodByName(gcType->ToTypeInstance(), "MarkAppendedObject", 1); + if (!markFromGCThreadMethodInstance) + return; + + SizedArray args; + args.push_back(mBfIRBuilder->CreateBitCast(markVal.mValue, mBfIRBuilder->MapType(mContext->mBfObjectType))); + BfExprEvaluator exprEvaluator(this); + exprEvaluator.CreateCall(NULL, markFromGCThreadMethodInstance.mMethodInstance, markFromGCThreadMethodInstance.mFunc, false, args); +} + void BfModule::EmitGCMarkValue(BfTypedValue markVal, BfModuleMethodInstance markFromGCThreadMethodInstance) { auto fieldType = markVal.mType; @@ -19048,7 +19241,7 @@ void BfModule::ProcessMethod_ProcessDeferredLocals(int startIdx) } } -void BfModule::EmitGCMarkValue(BfTypedValue& thisValue, BfType* checkType, int memberDepth, int curOffset, HashSet& objectOffsets, BfModuleMethodInstance markFromGCThreadMethodInstance) +void BfModule::EmitGCMarkValue(BfTypedValue& thisValue, BfType* checkType, int memberDepth, int curOffset, HashSet& objectOffsets, BfModuleMethodInstance markFromGCThreadMethodInstance, bool isAppendObject) { if (checkType->IsComposite()) PopulateType(checkType, BfPopulateType_Data); @@ -19184,7 +19377,10 @@ void BfModule::EmitGCMarkValue(BfTypedValue& thisValue, BfType* checkType, int m markValue = BfTypedValue(mBfIRBuilder->CreateBitCast(offsetValue, mBfIRBuilder->MapType(memberPtrType)), memberType, true); } - EmitGCMarkValue(markValue, markFromGCThreadMethodInstance); + if (isAppendObject) + EmitGCMarkAppended(markValue); + else + EmitGCMarkValue(markValue, markFromGCThreadMethodInstance); return; } @@ -19219,7 +19415,8 @@ void BfModule::EmitGCMarkValue(BfTypedValue& thisValue, BfType* checkType, int m if ((fieldDef->mDeclaringType->mTypeDeclaration != methodDef->mDeclaringType->mTypeDeclaration)) continue; } - EmitGCMarkValue(thisValue, fieldInst.mResolvedType, memberDepth + 1, curOffset + fieldInst.mDataOffset, objectOffsets, markFromGCThreadMethodInstance); + + EmitGCMarkValue(thisValue, fieldInst.mResolvedType, memberDepth + 1, curOffset + fieldInst.mDataOffset, objectOffsets, markFromGCThreadMethodInstance, fieldInst.IsAppendedObject()); } if ((typeInstance->mBaseType != NULL) && (typeInstance->mBaseType != mContext->mBfObjectType)) @@ -19373,11 +19570,25 @@ void BfModule::EmitGCMarkMembers() } else if (!fieldDef->mIsStatic) { - markVal = BfTypedValue(mBfIRBuilder->CreateInBoundsGEP(thisValue.mValue, 0, fieldInst.mDataIdx/*, fieldDef->mName*/), fieldInst.mResolvedType, true); + if (fieldInst.IsAppendedObject()) + { + auto fieldAddr = mBfIRBuilder->CreateInBoundsGEP(mCurMethodState->mLocals[0]->mValue, 0, fieldInst.mDataIdx); + auto val = mBfIRBuilder->CreateBitCast(fieldAddr, mBfIRBuilder->MapType(mContext->mBfObjectType)); + markVal = BfTypedValue(mBfIRBuilder->CreateBitCast(fieldAddr, mBfIRBuilder->MapType(fieldInst.mResolvedType)), fieldInst.mResolvedType); + } + else + { + markVal = BfTypedValue(mBfIRBuilder->CreateInBoundsGEP(thisValue.mValue, 0, fieldInst.mDataIdx/*, fieldDef->mName*/), fieldInst.mResolvedType, true); + } } if (markVal) - EmitGCMarkValue(markVal, markFromGCThreadMethodInstance); + { + if (fieldInst.IsAppendedObject()) + EmitGCMarkAppended(markVal); + else + EmitGCMarkValue(markVal, markFromGCThreadMethodInstance); + } } } } @@ -20881,15 +21092,47 @@ void BfModule::ProcessMethod(BfMethodInstance* methodInstance, bool isInlineDup, } } } + auto int8PtrType = CreatePointerType(GetPrimitiveType(BfTypeCode_Int8)); + int curSize = mCurTypeInstance->mInstSize; if (curSize > prevSize) - { - auto int8PtrType = CreatePointerType(GetPrimitiveType(BfTypeCode_Int8)); + { auto int8PtrVal = mBfIRBuilder->CreateBitCast(thisVal.mValue, mBfIRBuilder->MapType(int8PtrType)); int8PtrVal = mBfIRBuilder->CreateInBoundsGEP(int8PtrVal, GetConstValue(prevSize)); mBfIRBuilder->CreateMemSet(int8PtrVal, GetConstValue8(0), GetConstValue(curSize - prevSize), GetConstValue(mCurTypeInstance->mInstAlign)); } + if ((mCompiler->mOptions.mObjectHasDebugFlags) && (!mIsComptimeModule)) + { + auto useThis = mCurMethodState->mLocals[0]->mValue; + auto useThisType = mCurTypeInstance; + + auto checkTypeInst = mCurTypeInstance; + while (checkTypeInst != NULL) + { + for (auto& fieldInstance : checkTypeInst->mFieldInstances) + { + auto fieldDef = fieldInstance.GetFieldDef(); + if ((fieldDef == NULL) || (fieldDef->mIsStatic)) + continue; + if (fieldInstance.IsAppendedObject()) + { + if (checkTypeInst != useThisType) + { + useThis = mBfIRBuilder->CreateBitCast(useThis, mBfIRBuilder->MapType(checkTypeInst)); + useThisType = checkTypeInst; + } + + BfIRValue fieldAddr = mBfIRBuilder->CreateInBoundsGEP(useThis, 0, fieldInstance.mDataIdx); + auto int8PtrVal = mBfIRBuilder->CreateBitCast(fieldAddr, mBfIRBuilder->MapType(int8PtrType)); + mBfIRBuilder->CreateStore(GetConstValue8(BfObjectFlag_Deleted), int8PtrVal); + } + } + + checkTypeInst = checkTypeInst->mBaseType; + } + } + skipUpdateSrcPos = true; } else if (((methodDef->mMethodType == BfMethodType_Ctor) || (methodDef->mMethodType == BfMethodType_CtorNoBody)) && (!hasExternSpecifier)) diff --git a/IDEHelper/Compiler/BfModule.h b/IDEHelper/Compiler/BfModule.h index 10baa57d..47ab5df1 100644 --- a/IDEHelper/Compiler/BfModule.h +++ b/IDEHelper/Compiler/BfModule.h @@ -88,6 +88,7 @@ enum BfEvalExprFlags BfEvalExprFlags_FromConversionOp_Explicit = 0x10000000, BfEvalExprFlags_AllowGenericConstValue = 0x20000000, BfEvalExprFlags_IsExpressionBody = 0x40000000, + BfEvalExprFlags_AppendFieldInitializer = 0x80000000, BfEvalExprFlags_InheritFlags = BfEvalExprFlags_NoAutoComplete | BfEvalExprFlags_Comptime | BfEvalExprFlags_DeclType }; @@ -919,11 +920,13 @@ class BfConstResolveState public: BfMethodInstance* mMethodInstance; BfConstResolveState* mPrevConstResolveState; + bool mInCalcAppend; BfConstResolveState() { mMethodInstance = NULL; mPrevConstResolveState = NULL; + mInCalcAppend = false; } }; @@ -1840,6 +1843,7 @@ public: void CreateStaticField(BfFieldInstance* fieldInstance, bool isThreadLocal = false); void ResolveConstField(BfTypeInstance* typeInst, BfFieldInstance* fieldInstance, BfFieldDef* field, bool forceResolve = false); BfTypedValue GetFieldInitializerValue(BfFieldInstance* fieldInstance, BfExpression* initializer = NULL, BfFieldDef* fieldDef = NULL, BfType* fieldType = NULL, bool doStore = false); + void AppendedObjectInit(BfFieldInstance* fieldInstance); void MarkFieldInitialized(BfFieldInstance* fieldInstance); bool IsThreadLocal(BfFieldInstance* fieldInstance); BfType* ResolveVarFieldType(BfTypeInstance* typeInst, BfFieldInstance* fieldInstance, BfFieldDef* field); @@ -1967,7 +1971,7 @@ public: void AddMethodToWorkList(BfMethodInstance* methodInstance); bool IsInterestedInMethod(BfTypeInstance* typeInstance, BfMethodDef* methodDef); void CalcAppendAlign(BfMethodInstance* methodInst); - BfTypedValue TryConstCalcAppend(BfMethodInstance* methodInst, SizedArrayImpl& args); + BfTypedValue TryConstCalcAppend(BfMethodInstance* methodInst, SizedArrayImpl& args, bool force = false); BfTypedValue CallBaseCtorCalc(bool constOnly); void EmitCtorCalcAppend(); void CreateStaticCtor(); @@ -1980,7 +1984,8 @@ public: void EmitDtorBody(); void EmitEnumToStringBody(); void EmitTupleToStringBody(); - void EmitGCMarkValue(BfTypedValue& thisValue, BfType* checkType, int memberDepth, int curOffset, HashSet& objectOffsets, BfModuleMethodInstance markFromGCThreadMethodInstance); + void EmitGCMarkAppended(BfTypedValue markVal); + void EmitGCMarkValue(BfTypedValue& thisValue, BfType* checkType, int memberDepth, int curOffset, HashSet& objectOffsets, BfModuleMethodInstance markFromGCThreadMethodInstance, bool isAppendObject = false); void EmitGCMarkValue(BfTypedValue markVal, BfModuleMethodInstance markFromGCThreadMethodInstance); void EmitGCMarkMembers(); void EmitGCFindTLSMembers(); diff --git a/IDEHelper/Compiler/BfModuleTypeUtils.cpp b/IDEHelper/Compiler/BfModuleTypeUtils.cpp index b0fe8aac..271edc49 100644 --- a/IDEHelper/Compiler/BfModuleTypeUtils.cpp +++ b/IDEHelper/Compiler/BfModuleTypeUtils.cpp @@ -820,6 +820,9 @@ void BfModule::InitType(BfType* resolvedTypeRef, BfPopulateType populateType) void BfModule::AddFieldDependency(BfTypeInstance* typeInstance, BfFieldInstance* fieldInstance, BfType* fieldType) { auto depFlag = fieldType->IsValueType() ? BfDependencyMap::DependencyFlag_ValueTypeMemberData : BfDependencyMap::DependencyFlag_PtrMemberData; + if (fieldInstance->IsAppendedObject()) + depFlag = BfDependencyMap::DependencyFlag_ValueTypeMemberData; + AddDependency(fieldType, typeInstance, depFlag); if ((fieldType->IsStruct()) && (fieldType->IsGenericTypeInstance())) @@ -4565,6 +4568,12 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy auto initializer = field->GetInitializer(); + if ((field->mIsAppend) && (!resolvedTypeRef->IsObject())) + Fail("Appended objects can only be declared in class types", field->GetFieldDeclaration()->mExternSpecifier, true); + + if ((field->mIsAppend) && (isUnion)) + Fail("Appended objects cannot be declared in unions", field->GetFieldDeclaration()->mExternSpecifier, true); + if (field->IsEnumCaseEntry()) { if (typeInstance->IsEnum()) @@ -5109,6 +5118,83 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy int dataSize = resolvedFieldType->mSize; int alignSize = resolvedFieldType->mAlign; + + if (fieldInstance->IsAppendedObject()) + { + SetAndRestoreValue prevTypeRef(mContext->mCurTypeState->mCurFieldDef, fieldDef); + SetAndRestoreValue prevResolveKind(mContext->mCurTypeState->mResolveKind, BfTypeState::ResolveKind_FieldType); + + auto fieldTypeInst = resolvedFieldType->ToTypeInstance(); + dataSize = fieldTypeInst->mInstSize; + alignSize = fieldTypeInst->mInstAlign; + + if ((typeInstance != NULL) && (fieldTypeInst->mTypeDef->mIsAbstract)) + { + Fail("Cannot create an instance of an abstract class", nameRefNode); + } + + SetAndRestoreValue prevIgnoreWrites(mBfIRBuilder->mIgnoreWrites, true); + BfMethodState methodState; + SetAndRestoreValue prevMethodState(mCurMethodState, &methodState); + methodState.mTempKind = BfMethodState::TempKind_NonStatic; + + BfTypedValue appendIndexValue; + BfExprEvaluator exprEvaluator(this); + + BfResolvedArgs resolvedArgs; + + auto fieldDecl = fieldDef->GetFieldDeclaration(); + if (auto invocationExpr = BfNodeDynCast(fieldDecl->mInitializer)) + { + resolvedArgs.Init(invocationExpr->mOpenParen, &invocationExpr->mArguments, &invocationExpr->mCommas, invocationExpr->mCloseParen); + exprEvaluator.ResolveArgValues(resolvedArgs, BfResolveArgsFlag_DeferParamEval); + } + + BfFunctionBindResult bindResult; + bindResult.mSkipThis = true; + bindResult.mWantsArgs = true; + SetAndRestoreValue prevBindResult(exprEvaluator.mFunctionBindResult, &bindResult); + + BfTypedValue emptyThis(mBfIRBuilder->GetFakeVal(), resolvedTypeRef, resolvedTypeRef->IsStruct()); + + exprEvaluator.mBfEvalExprFlags = BfEvalExprFlags_Comptime; + auto ctorResult = exprEvaluator.MatchConstructor(nameRefNode, NULL, emptyThis, fieldTypeInst, resolvedArgs, false, true); + + if ((bindResult.mMethodInstance != NULL) && (bindResult.mMethodInstance->mMethodDef->mHasAppend)) + { + auto calcAppendMethodModule = GetMethodInstanceAtIdx(bindResult.mMethodInstance->GetOwner(), bindResult.mMethodInstance->mMethodDef->mIdx + 1, BF_METHODNAME_CALCAPPEND); + + SizedArray irArgs; + if (bindResult.mIRArgs.size() > 1) + irArgs.Insert(0, &bindResult.mIRArgs[1], bindResult.mIRArgs.size() - 1); + BfTypedValue appendSizeTypedValue = TryConstCalcAppend(calcAppendMethodModule.mMethodInstance, irArgs, true); + if (appendSizeTypedValue) + { + int appendAlign = calcAppendMethodModule.mMethodInstance->mAppendAllocAlign; + dataSize = BF_ALIGN(dataSize, appendAlign); + alignSize = BF_MAX(alignSize, appendAlign); + + auto constant = mBfIRBuilder->GetConstant(appendSizeTypedValue.mValue); + if (constant != NULL) + { + dataSize += constant->mInt32; + } + } + else + { + Fail(StrFormat("Append constructor '%s' does not result in a constant size", MethodToString(bindResult.mMethodInstance).c_str()), nameRefNode); + } + + } + } + else if (fieldDef->mIsAppend) + { + if (typeInstance->IsObject()) + Fail("Append fields can only be declared in classes", nameRefNode, true); + else if ((!resolvedFieldType->IsObject()) && (!resolvedFieldType->IsGenericParam())) + Fail("Append fields must be classes", nameRefNode, true); + } + fieldInstance->mDataSize = dataSize; if (!isUnion) { @@ -5244,7 +5330,7 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy alignBuckets[alignBits].RemoveAt(0); dataFieldVec.push_back(fieldInst); curSize = BF_ALIGN(curSize, fieldInst->GetAlign(packing)); - curSize += fieldInst->mResolvedType->mSize; + curSize += fieldInst->mDataSize; foundEntry = true; if (!isHighestBucket) @@ -5265,7 +5351,7 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy alignBuckets[alignBits].RemoveAt(0); dataFieldVec.push_back(fieldInst); curSize = BF_ALIGN(curSize, fieldInst->GetAlign(packing)); - curSize += fieldInst->mResolvedType->mSize; + curSize += fieldInst->mDataSize; break; } } @@ -5281,9 +5367,12 @@ void BfModule::DoPopulateType(BfType* resolvedTypeRef, BfPopulateType populateTy auto resolvedFieldType = fieldInstance->GetResolvedType(); BF_ASSERT(resolvedFieldType->mSize >= 0); - int dataSize = resolvedFieldType->mSize; + + if (fieldInstance->mDataSize == 0) + fieldInstance->mDataSize = resolvedFieldType->mSize; + + int dataSize = fieldInstance->mDataSize; int alignSize = fieldInstance->GetAlign(packing); - fieldInstance->mDataSize = dataSize; int nextDataPos = dataPos; nextDataPos = (dataPos + (alignSize - 1)) & ~(alignSize - 1); diff --git a/IDEHelper/Compiler/BfReducer.cpp b/IDEHelper/Compiler/BfReducer.cpp index ec1f9ee9..9eedbf12 100644 --- a/IDEHelper/Compiler/BfReducer.cpp +++ b/IDEHelper/Compiler/BfReducer.cpp @@ -6319,6 +6319,7 @@ BfAstNode* BfReducer::ReadTypeMember(BfTokenNode* tokenNode, bool declStarted, i case BfToken_Override: case BfToken_Abstract: case BfToken_Concrete: + case BfToken_Append: case BfToken_Extern: case BfToken_New: case BfToken_Implicit: @@ -6575,6 +6576,30 @@ BfAstNode* BfReducer::ReadTypeMember(BfTokenNode* tokenNode, bool declStarted, i if (token == BfToken_Extern) { + if ((fieldDecl->mExternSpecifier != NULL) && (fieldDecl->mExternSpecifier->mToken == BfToken_Append)) + { + Fail("Extern cannot be used with 'append' specified", tokenNode); + } + else if (fieldDecl->mExternSpecifier != NULL) + { + Fail("Extern already specified", tokenNode); + } + + MEMBER_SET(fieldDecl, mExternSpecifier, tokenNode); + handled = true; + } + + if (token == BfToken_Append) + { + if ((fieldDecl->mExternSpecifier != NULL) && (fieldDecl->mExternSpecifier->mToken == BfToken_Extern)) + { + Fail("Append cannot be used with 'extern' specified", tokenNode); + } + else if (fieldDecl->mExternSpecifier != NULL) + { + Fail("Append already specified", tokenNode); + } + MEMBER_SET(fieldDecl, mExternSpecifier, tokenNode); handled = true; } diff --git a/IDEHelper/Compiler/BfResolvedTypeUtils.cpp b/IDEHelper/Compiler/BfResolvedTypeUtils.cpp index edbb8072..5dabfdeb 100644 --- a/IDEHelper/Compiler/BfResolvedTypeUtils.cpp +++ b/IDEHelper/Compiler/BfResolvedTypeUtils.cpp @@ -498,6 +498,8 @@ void BfFieldInstance::GetDataRange(int& dataIdx, int& dataCount) int BfFieldInstance::GetAlign(int packing) { int align = mResolvedType->mAlign; + if (IsAppendedObject()) + align = mResolvedType->ToTypeInstance()->mInstAlign; if (packing > 0) align = BF_MIN(align, packing); if (mCustomAttributes != NULL) @@ -528,6 +530,12 @@ int BfFieldInstance::GetAlign(int packing) return align; } +bool BfFieldInstance::IsAppendedObject() +{ + auto fieldDef = GetFieldDef(); + return (fieldDef != NULL) && (fieldDef->mIsAppend) && (mResolvedType->IsObject()) && (mOwner->IsObject()); +} + ////////////////////////////////////////////////////////////////////////// int64 BfDeferredMethodCallData::GenerateMethodId(BfModule* module, int64 methodId) diff --git a/IDEHelper/Compiler/BfResolvedTypeUtils.h b/IDEHelper/Compiler/BfResolvedTypeUtils.h index 323001db..21e0510e 100644 --- a/IDEHelper/Compiler/BfResolvedTypeUtils.h +++ b/IDEHelper/Compiler/BfResolvedTypeUtils.h @@ -1469,6 +1469,7 @@ public: void SetResolvedType(BfType* type); void GetDataRange(int& dataIdx, int& dataCount); int GetAlign(int packing); + bool IsAppendedObject(); }; enum BfMethodRefKind diff --git a/IDEHelper/Compiler/BfStmtEvaluator.cpp b/IDEHelper/Compiler/BfStmtEvaluator.cpp index 7580a651..2b8ebcc6 100644 --- a/IDEHelper/Compiler/BfStmtEvaluator.cpp +++ b/IDEHelper/Compiler/BfStmtEvaluator.cpp @@ -1929,6 +1929,13 @@ BfLocalVariable* BfModule::HandleVariableDeclaration(BfVariableDeclaration* varD BfLocalVariable* localVar = AddLocalVariableDef(localDef, true, false, BfIRValue(), initType); if (wantsStore) mBfIRBuilder->CreateAlignedStore(initValue.mValue, localVar->mAddr, localVar->mResolvedType->mAlign); + + if ((mCurMethodState->mConstResolveState != NULL) && (mCurMethodState->mConstResolveState->mInCalcAppend)) + { + if (localDef->mValue.IsConst()) + localDef->mConstValue = localDef->mValue; + } + return localVar; } diff --git a/IDEHelper/Compiler/BfSystem.h b/IDEHelper/Compiler/BfSystem.h index ee2b7f3a..4306c478 100644 --- a/IDEHelper/Compiler/BfSystem.h +++ b/IDEHelper/Compiler/BfSystem.h @@ -610,6 +610,7 @@ public: bool mIsInline; bool mIsVolatile; bool mIsExtern; + bool mIsAppend; bool mIsProperty; BfAstNode* mFieldDeclaration; // It may seem that fields and properties don't need a 'mNextWithSameName', but with extensions it's possible @@ -625,6 +626,7 @@ public: mUsingProtection = BfProtection_Hidden; mIsInline = false; mIsExtern = false; + mIsAppend = false; mIsVolatile = false; mIsProperty = false; mFieldDeclaration = NULL; @@ -1680,7 +1682,8 @@ enum BfFieldFlags BfFieldFlags_EnumPayload = 0x100, BfFieldFlags_EnumDiscriminator = 0x200, BfFieldFlags_EnumCase = 0x400, - BfFieldFlags_ReadOnly = 0x800 + BfFieldFlags_ReadOnly = 0x800, + BfFieldFlags_Appended = 0x1000 }; enum BfReflectKind diff --git a/IDEHelper/Compiler/CeMachine.cpp b/IDEHelper/Compiler/CeMachine.cpp index 60007fb6..ba92df8c 100644 --- a/IDEHelper/Compiler/CeMachine.cpp +++ b/IDEHelper/Compiler/CeMachine.cpp @@ -5667,6 +5667,11 @@ bool CeContext::Execute(CeFunction* startFunction, uint8* startStackPtr, uint8* Fail(_GetCurFrame(), "Array out of bounds"); return false; } + else if (checkFunction->mFunctionKind == CeFunctionKind_OOB) + { + Fail(_GetCurFrame(), "Object not initialized"); + return false; + } else if (checkFunction->mFunctionKind == CeFunctionKind_Malloc) { int64 size; @@ -9340,6 +9345,8 @@ void CeMachine::CheckFunctionKind(CeFunction* ceFunction) { if (methodDef->mName == "ThrowIndexOutOfRange") ceFunction->mFunctionKind = CeFunctionKind_OOB; + else if (methodDef->mName == "ThrowObjectNotInitialized") + ceFunction->mFunctionKind = CeFunctionKind_ObjectNotInitialized; else if (methodDef->mName == "FatalError") ceFunction->mFunctionKind = CeFunctionKind_FatalError; else if (methodDef->mName == "Dbg_RawAlloc") diff --git a/IDEHelper/Compiler/CeMachine.h b/IDEHelper/Compiler/CeMachine.h index 0453d1de..297c90ed 100644 --- a/IDEHelper/Compiler/CeMachine.h +++ b/IDEHelper/Compiler/CeMachine.h @@ -422,6 +422,7 @@ enum CeFunctionKind CeFunctionKind_Normal, CeFunctionKind_Extern, CeFunctionKind_OOB, + CeFunctionKind_ObjectNotInitialized, CeFunctionKind_Malloc, CeFunctionKind_Free, CeFunctionKind_DynCheckFailed, diff --git a/IDEHelper/Tests/src/Append.bf b/IDEHelper/Tests/src/Append.bf index 6d166c47..34870f76 100644 --- a/IDEHelper/Tests/src/Append.bf +++ b/IDEHelper/Tests/src/Append.bf @@ -76,6 +76,13 @@ namespace Tests } } + class ClassF + { + public int mA = 123; + public append String mB = .(mA); + public int mC = 234; + } + static void CheckData(Object obj, int lastAllocSize, uint8[] data) { int objSize = typeof(Object).InstanceSize; @@ -110,6 +117,17 @@ namespace Tests 0x80, 0x70, 0x60, 0x50, 0x40, 0x30, 0x20, 0x10 )); delete:trackedAlloc ce; + + int sizeDiff = Math.Abs(typeof(ClassF).InstanceSize - (1024 + sizeof(int)*5)); + Test.Assert(sizeDiff < 32); + + ClassF cf = scope .(); + cf.mB.Append("Abc"); + Test.Assert(cf.mA == 123); + Test.Assert(cf.mB == "Abc"); + Test.Assert(cf.mB.AllocSize == 1024); + Test.Assert(cf.mC == 234); + cf.mB.Append('!', 2048); } } }